| /* |
| * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * 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. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /**======================================================================== |
| |
| \file wlan_hdd_cfg80211.c |
| |
| \brief WLAN Host Device Driver implementation |
| |
| ========================================================================*/ |
| |
| /**========================================================================= |
| |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| |
| $Header:$ $DateTime: $ $Author: $ |
| |
| |
| when who what, where, why |
| -------- --- -------------------------------------------------------- |
| 21/12/09 Ashwani Created module. |
| |
| 07/06/10 Kumar Deepak Implemented cfg80211 callbacks for ANDROID |
| Ganesh K |
| ==========================================================================*/ |
| |
| |
| #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 <wlan_hdd_includes.h> |
| #include <net/arp.h> |
| #include <net/cfg80211.h> |
| #include <vos_trace.h> |
| #include "vos_cnss.h" |
| #include <linux/wireless.h> |
| #include <wlan_hdd_wowl.h> |
| #include <aniGlobal.h> |
| #include "ccmApi.h" |
| #include "sirParams.h" |
| #include "dot11f.h" |
| #include "wlan_hdd_assoc.h" |
| #include "wlan_hdd_wext.h" |
| #include "sme_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_assoc.h" |
| #include "wlan_hdd_power.h" |
| #include "wlan_hdd_trace.h" |
| #include "vos_types.h" |
| #include "vos_trace.h" |
| #include "vos_utils.h" |
| #include "vos_sched.h" |
| #include <qc_sap_ioctl.h> |
| #include "wlan_hdd_tdls.h" |
| #include "wlan_hdd_wmm.h" |
| #include "wlan_qct_wda.h" |
| #include "wlan_nv.h" |
| #include "wlan_hdd_dev_pwr.h" |
| #include "hif.h" |
| #include "wma.h" |
| #include "wlan_hdd_misc.h" |
| #ifdef WLAN_FEATURE_NAN |
| #include "nan_Api.h" |
| #include "wlan_hdd_nan.h" |
| #endif |
| #ifdef IPA_OFFLOAD |
| #include <wlan_hdd_ipa.h> |
| #endif |
| #include "wlan_hdd_mdns_offload.h" |
| #include "wlan_hdd_ocb.h" |
| #include "qwlan_version.h" |
| |
| #include "wlan_logging_sock_svc.h" |
| #include "sapApi.h" |
| #include "csrApi.h" |
| |
| #include "wmi_unified_priv.h" |
| |
| #define g_mode_rates_size (12) |
| #define a_mode_rates_size (8) |
| #define FREQ_BASE_80211G (2407) |
| #define FREQ_BAND_DIFF_80211G (5) |
| #define MAX_SCAN_SSID 10 |
| #define MAX_PENDING_LOG 5 |
| #define MAX_HT_MCS_IDX 8 |
| #define MAX_VHT_MCS_IDX 10 |
| #define INVALID_MCS_IDX 255 |
| #define GET_IE_LEN_IN_BSS_DESC(lenInBss) ( lenInBss + sizeof(lenInBss) - \ |
| ((uintptr_t)OFFSET_OF( tSirBssDescription, ieFields))) |
| /* |
| * Android CTS verifier needs atleast this much wait time (in msec) |
| */ |
| #define MAX_REMAIN_ON_CHANNEL_DURATION (5000) |
| #define HDD_WAKE_LOCK_SCAN_DURATION (5 * 1000) /* in msec */ |
| |
| #define WLAN_HDD_TGT_NOISE_FLOOR_DBM (-96) |
| |
| /* |
| * max_sched_scan_plans defined to 2 for |
| * (1)fast scan |
| * (2)slow scan |
| */ |
| #define MAX_SCHED_SCAN_PLANS 2 |
| |
| /* For IBSS, enable obss, fromllb, overlapOBSS & overlapFromllb protection |
| check. The bit map is defined in: |
| |
| typedef struct sCfgProtection |
| { |
| tANI_U32 overlapFromlla:1; |
| tANI_U32 overlapFromllb:1; |
| tANI_U32 overlapFromllg:1; |
| tANI_U32 overlapHt20:1; |
| tANI_U32 overlapNonGf:1; |
| tANI_U32 overlapLsigTxop:1; |
| tANI_U32 overlapRifs:1; |
| tANI_U32 overlapOBSS:1; |
| tANI_U32 fromlla:1; |
| tANI_U32 fromllb:1; |
| tANI_U32 fromllg:1; |
| tANI_U32 ht20:1; |
| tANI_U32 nonGf:1; |
| tANI_U32 lsigTxop:1; |
| tANI_U32 rifs:1; |
| tANI_U32 obss:1; |
| }tCfgProtection, *tpCfgProtection; |
| |
| */ |
| #define IBSS_CFG_PROTECTION_ENABLE_MASK 0x8282 |
| |
| #define HDD2GHZCHAN(freq, chan, flag) { \ |
| .band = IEEE80211_BAND_2GHZ, \ |
| .center_freq = (freq), \ |
| .hw_value = (chan),\ |
| .flags = (flag), \ |
| .max_antenna_gain = 0 ,\ |
| .max_power = 30, \ |
| } |
| |
| #define HDD5GHZCHAN(freq, chan, flag) { \ |
| .band = IEEE80211_BAND_5GHZ, \ |
| .center_freq = (freq), \ |
| .hw_value = (chan),\ |
| .flags = (flag), \ |
| .max_antenna_gain = 0 ,\ |
| .max_power = 30, \ |
| } |
| |
| #define HDD_G_MODE_RATETAB(rate, rate_id, flag)\ |
| {\ |
| .bitrate = rate, \ |
| .hw_value = rate_id, \ |
| .flags = flag, \ |
| } |
| |
| #ifdef WLAN_FEATURE_VOWIFI_11R |
| #define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 |
| #define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 |
| #endif |
| |
| #define MAX_TXPOWER_SCALE 4 |
| |
| #define HDD_CHANNEL_14 14 |
| #define WLAN_HDD_MAX_FEATURE_SET 8 |
| |
| #define IS_DFS_MODE_VALID(mode) ((mode >= DFS_MODE_NONE && mode <= DFS_MODE_DEPRIORITIZE)) |
| |
| #ifdef FEATURE_WLAN_EXTSCAN |
| /* |
| * Used to allocate the size of 4096 for the EXTScan NL data. |
| * The size of 4096 is considered assuming that all data per |
| * respective event fit with in the limit.Please take a call |
| * on the limit based on the data requirements. |
| */ |
| |
| #define EXTSCAN_EVENT_BUF_SIZE 4096 |
| #endif |
| |
| #ifdef WLAN_FEATURE_LINK_LAYER_STATS |
| /* |
| * Used to allocate the size of 4096 for the link layer stats. |
| * The size of 4096 is considered assuming that all data per |
| * respective event fit with in the limit.Please take a call |
| * on the limit based on the data requirements on link layer |
| * statistics. |
| */ |
| #define LL_STATS_EVENT_BUF_SIZE 4096 |
| #endif |
| |
| /* EXT TDLS */ |
| /* |
| * Used to allocate the size of 4096 for the TDLS. |
| * The size of 4096 is considered assuming that all data per |
| * respective event fit with in the limit.Please take a call |
| * on the limit based on the data requirements on link layer |
| * statistics. |
| */ |
| #define EXTTDLS_EVENT_BUF_SIZE 4096 |
| |
| /* (30 Mins) */ |
| #define MIN_TIME_REQUIRED_FOR_NEXT_BUG_REPORT (30 * 60 * 1000) |
| |
| /* |
| * Count to ratelimit the HDD logs during Scan and connect |
| */ |
| #define HDD_SCAN_REJECT_RATE_LIMIT 5 |
| |
| 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 |
| #ifdef WLAN_FEATURE_11W |
| WLAN_CIPHER_SUITE_AES_CMAC, |
| #endif |
| }; |
| |
| const static 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) , |
| }; |
| |
| const static 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) , |
| #ifdef FEATURE_WLAN_CH144 |
| HDD5GHZCHAN(5720,144, 0) , |
| #endif /* FEATURE_WLAN_CH144 */ |
| HDD5GHZCHAN(5745,149, 0) , |
| HDD5GHZCHAN(5765,153, 0) , |
| HDD5GHZCHAN(5785,157, 0) , |
| HDD5GHZCHAN(5805,161, 0) , |
| HDD5GHZCHAN(5825,165, 0) , |
| #ifndef FEATURE_STATICALLY_ADD_11P_CHANNELS |
| 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) , |
| #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 = IEEE80211_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, |
| .vht_cap.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
| | IEEE80211_VHT_CAP_SHORT_GI_80 |
| | IEEE80211_VHT_CAP_TXSTBC |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS) |
| | (IEEE80211_VHT_CAP_RXSTBC_MASK & |
| ( IEEE80211_VHT_CAP_RXSTBC_1 |
| | IEEE80211_VHT_CAP_RXSTBC_2)) |
| #endif |
| | IEEE80211_VHT_CAP_RXLDPC, |
| }; |
| |
| static struct ieee80211_supported_band wlan_hdd_band_5_GHZ = |
| { |
| .channels = NULL, |
| .n_channels = ARRAY_SIZE(hdd_channels_5_GHZ), |
| .band = IEEE80211_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, |
| .vht_cap.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
| | IEEE80211_VHT_CAP_SHORT_GI_80 |
| | IEEE80211_VHT_CAP_TXSTBC |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) |
| | (IEEE80211_VHT_CAP_RXSTBC_MASK & |
| ( IEEE80211_VHT_CAP_RXSTBC_1 |
| | IEEE80211_VHT_CAP_RXSTBC_2)) |
| #endif |
| | IEEE80211_VHT_CAP_RXLDPC |
| }; |
| |
| /* 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_TIME_ADVERT) | |
| BIT(SIR_MAC_MGMT_PROBE_REQ), |
| }, |
| [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_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), |
| }, |
| }; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS) |
| /* 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), |
| }, |
| }; |
| |
| /* ADHOC (IBSS) limit */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_adhoc_iface_limit[] = { |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_STATION), |
| }, |
| { |
| .max = 1, |
| .types = BIT(NL80211_IFTYPE_ADHOC), |
| }, |
| }; |
| |
| /* |
| * AP ( + AP) combination or |
| * AP ( + AP + AP + AP) combination if 4-SAP is supported |
| * (WLAN_4SAP_CONCURRENCY) |
| */ |
| static const struct ieee80211_iface_limit |
| wlan_hdd_ap_iface_limit[] = { |
| { |
| .max = (VOS_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 = VOS_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) |
| } |
| }; |
| |
| 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), |
| }, |
| /* ADHOC */ |
| { |
| .limits = wlan_hdd_adhoc_iface_limit, |
| .num_different_channels = 1, |
| .max_interfaces = 2, |
| .n_limits = ARRAY_SIZE(wlan_hdd_adhoc_iface_limit), |
| }, |
| /* AP */ |
| { |
| .limits = wlan_hdd_ap_iface_limit, |
| .num_different_channels = 2, |
| .max_interfaces = (SAP_MAX_OBSS_STA_CNT + |
| VOS_MAX_NO_OF_SAP_MODE), |
| .n_limits = ARRAY_SIZE(wlan_hdd_ap_iface_limit), |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) || defined(BEACON_INTV_BACKPORTS) |
| .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 + |
| VOS_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(BEACON_INTV_BACKPORTS) |
| .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 concurrent 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, |
| }, |
| /* Monitor */ |
| { |
| .limits = wlan_hdd_mon_iface_limit, |
| .max_interfaces = 3, |
| .num_different_channels = 2, |
| .n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit), |
| }, |
| }; |
| #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || |
| defined(WITH_BACKPORTS) */ |
| |
| |
| static struct cfg80211_ops wlan_hdd_cfg80211_ops; |
| struct hdd_bpf_context bpf_context; |
| |
| /* Data rate 100KBPS based on IE Index */ |
| struct index_data_rate_type |
| { |
| v_U8_t beacon_rate_index; |
| v_U16_t supported_rate[4]; |
| }; |
| |
| /* 11B, 11G Rate table include Basic rate and Extended rate |
| The IDX field is the rate index |
| The HI field is the rate when RSSI is strong or being ignored |
| (in this case we report actual rate) |
| The MID field is the rate when RSSI is moderate |
| (in this case we cap 11b rates at 5.5 and 11g rates at 24) |
| The LO field is the rate when RSSI is low |
| (in this case we don't report rates, actual current rate used) |
| */ |
| static const struct |
| { |
| v_U8_t beacon_rate_index; |
| v_U16_t supported_rate[4]; |
| } supported_data_rate[] = |
| { |
| /* IDX HI HM LM LO (RSSI-based index */ |
| {2, { 10, 10, 10, 0}}, |
| {4, { 20, 20, 10, 0}}, |
| {11, { 55, 20, 10, 0}}, |
| {12, { 60, 55, 20, 0}}, |
| {18, { 90, 55, 20, 0}}, |
| {22, {110, 55, 20, 0}}, |
| {24, {120, 90, 60, 0}}, |
| {36, {180, 120, 60, 0}}, |
| {44, {220, 180, 60, 0}}, |
| {48, {240, 180, 90, 0}}, |
| {66, {330, 180, 90, 0}}, |
| {72, {360, 240, 90, 0}}, |
| {96, {480, 240, 120, 0}}, |
| {108, {540, 240, 120, 0}} |
| }; |
| |
| /* MCS Based rate table */ |
| /* HT MCS parameters with Nss = 1 */ |
| static struct index_data_rate_type supported_mcs_rate_nss1[] = |
| { |
| /* MCS L20 L40 S20 S40 */ |
| {0, {65, 135, 72, 150}}, |
| {1, {130, 270, 144, 300}}, |
| {2, {195, 405, 217, 450}}, |
| {3, {260, 540, 289, 600}}, |
| {4, {390, 810, 433, 900}}, |
| {5, {520, 1080, 578, 1200}}, |
| {6, {585, 1215, 650, 1350}}, |
| {7, {650, 1350, 722, 1500}} |
| }; |
| /* HT MCS parameters with Nss = 2 */ |
| static struct index_data_rate_type supported_mcs_rate_nss2[] = |
| { |
| /* MCS L20 L40 S20 S40 */ |
| {0, {130, 270, 144, 300}}, |
| {1, {260, 540, 289, 600}}, |
| {2, {390, 810, 433, 900}}, |
| {3, {520, 1080, 578, 1200}}, |
| {4, {780, 1620, 867, 1800}}, |
| {5, {1040, 2160, 1156, 2400}}, |
| {6, {1170, 2430, 1300, 2700}}, |
| {7, {1300, 2700, 1444, 3000}} |
| }; |
| |
| #ifdef WLAN_FEATURE_11AC |
| |
| #define DATA_RATE_11AC_MCS_MASK 0x03 |
| |
| struct index_vht_data_rate_type |
| { |
| v_U8_t beacon_rate_index; |
| v_U16_t supported_VHT80_rate[2]; |
| v_U16_t supported_VHT40_rate[2]; |
| v_U16_t supported_VHT20_rate[2]; |
| }; |
| |
| typedef enum |
| { |
| DATA_RATE_11AC_MAX_MCS_7, |
| DATA_RATE_11AC_MAX_MCS_8, |
| DATA_RATE_11AC_MAX_MCS_9, |
| DATA_RATE_11AC_MAX_MCS_NA |
| } eDataRate11ACMaxMcs; |
| |
| /* SSID broadcast type */ |
| typedef enum eSSIDBcastType |
| { |
| eBCAST_UNKNOWN = 0, |
| eBCAST_NORMAL = 1, |
| eBCAST_HIDDEN = 2, |
| } tSSIDBcastType; |
| |
| /* MCS Based VHT rate table */ |
| /* MCS parameters with Nss = 1*/ |
| static struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = |
| { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {293, 325}, {135, 150}, {65, 72}}, |
| {1, {585, 650}, {270, 300}, {130, 144}}, |
| {2, {878, 975}, {405, 450}, {195, 217}}, |
| {3, {1170, 1300}, {540, 600}, {260, 289}}, |
| {4, {1755, 1950}, {810, 900}, {390, 433}}, |
| {5, {2340, 2600}, {1080, 1200}, {520, 578}}, |
| {6, {2633, 2925}, {1215, 1350}, {585, 650}}, |
| {7, {2925, 3250}, {1350, 1500}, {650, 722}}, |
| {8, {3510, 3900}, {1620, 1800}, {780, 867}}, |
| {9, {3900, 4333}, {1800, 2000}, {780, 867}} |
| }; |
| |
| /*MCS parameters with Nss = 2*/ |
| static struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = |
| { |
| /* MCS L80 S80 L40 S40 L20 S40*/ |
| {0, {585, 650}, {270, 300}, {130, 144}}, |
| {1, {1170, 1300}, {540, 600}, {260, 289}}, |
| {2, {1755, 1950}, {810, 900}, {390, 433}}, |
| {3, {2340, 2600}, {1080, 1200}, {520, 578}}, |
| {4, {3510, 3900}, {1620, 1800}, {780, 867}}, |
| {5, {4680, 5200}, {2160, 2400}, {1040, 1156}}, |
| {6, {5265, 5850}, {2430, 2700}, {1170, 1300}}, |
| {7, {5850, 6500}, {2700, 3000}, {1300, 1444}}, |
| {8, {7020, 7800}, {3240, 3600}, {1560, 1733}}, |
| {9, {7800, 8667}, {3600, 4000}, {1560, 1733}} |
| }; |
| #endif /* WLAN_FEATURE_11AC */ |
| |
| /* Array index points to MCS and array value points respective rssi */ |
| static int rssiMcsTbl[][10] = |
| { |
| /*MCS 0 1 2 3 4 5 6 7 8 9*/ |
| {-82, -79, -77, -74, -70, -66, -65, -64, -59, -57}, //20 |
| {-79, -76, -74, -71, -67, -63, -62, -61, -56, -54}, //40 |
| {-76, -73, -71, -68, -64, -60, -59, -58, -53, -51} //80 |
| }; |
| |
| extern struct net_device_ops net_ops_struct; |
| |
| /** |
| * struct cfg_hostapd_edca - Store hostapd EDCA params |
| * and fill them in gLimEdcaParams |
| * structure |
| * @acm: EDCA param |
| * @aifs: EDCA param |
| * @cwmin: EDCA param |
| * @cwmax: EDCA param |
| * @txop: EDCA param |
| * @paramb: EDCA param for 11b |
| * @paramg: EDCA param for 11g |
| * @enable: enable hostapd EDCA params |
| */ |
| struct cfg_hostapd_edca { |
| uint8_t acm; |
| uint8_t aifs; |
| uint16_t cwmin; |
| uint16_t cwmax; |
| uint8_t txop; |
| uint8_t paramsb[5]; |
| uint8_t paramsg[5]; |
| uint8_t enable; |
| }; |
| |
| #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 |
| |
| enum wlan_hdd_vendor_ie_access_policy { |
| WLAN_HDD_VENDOR_IE_ACCESS_NONE = 0, |
| WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED, |
| }; |
| |
| 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 */ |
| |
| #ifdef FEATURE_WLAN_EXTSCAN |
| |
| static const struct nla_policy |
| wlan_hdd_extscan_config_policy[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CLASS] = { .type = NLA_U8 }, |
| |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH] = { .type = NLA_U8 }, |
| |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID] = { |
| .type = NLA_UNSPEC, |
| .len = HDD_MAC_ADDR_LEN}, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW] = { .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH] = { .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_CHANNEL] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_SSID] = { .type = NLA_BINARY, |
| .len = IEEE80211_MAX_SSID_LEN + 1 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_NUM_SSID] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_BAND] = { .type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW] = { .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH] = { .type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS] = { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE] = { .type = NLA_U32 }, |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_pno_config_policy[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID] = { |
| .type = NLA_BINARY, |
| .len = IEEE80211_MAX_SSID_LEN + 1 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS] = { |
| .type = NLA_U8 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT] = { |
| .type = NLA_U8 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID] = { |
| .type = NLA_U32 |
| }, |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_extscan_results_policy[QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_MAX + 1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD] = { .type = NLA_U16 }, |
| [QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY] = { .type = NLA_U16 }, |
| }; |
| |
| |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS) |
| 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 |
| |
| #if defined(FEATURE_WLAN_CH_AVOID) || defined(FEATURE_WLAN_FORCE_SAP_SCC) |
| /* |
| * FUNCTION: wlan_hdd_send_avoid_freq_event |
| * This is called when wlan driver needs to send vendor specific |
| * avoid frequency range event to user space |
| */ |
| int wlan_hdd_send_avoid_freq_event(hdd_context_t *pHddCtx, |
| tHddAvoidFreqList *pAvoidFreqList) |
| { |
| struct sk_buff *vendor_event; |
| |
| ENTER(); |
| |
| if (!pHddCtx) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: HDD context is null", __func__); |
| return -1; |
| } |
| |
| if (!pAvoidFreqList) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: pAvoidFreqList is null", __func__); |
| return -1; |
| } |
| |
| vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy, |
| NULL, |
| sizeof(tHddAvoidFreqList), |
| QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX, |
| GFP_KERNEL); |
| if (!vendor_event) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: cfg80211_vendor_event_alloc failed", __func__); |
| return -1; |
| } |
| |
| memcpy(skb_put(vendor_event, sizeof(tHddAvoidFreqList)), |
| (void *)pAvoidFreqList, sizeof(tHddAvoidFreqList)); |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| |
| EXIT(); |
| return 0; |
| } |
| #endif /* FEATURE_WLAN_CH_AVOID || FEATURE_WLAN_FORCE_SAP_SCC */ |
| |
| #ifdef WLAN_FEATURE_NAN |
| /** |
| * __wlan_hdd_cfg80211_nan_request() - handle NAN request |
| * @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 send a NAN request to |
| * firmware. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_nan_request(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| |
| { |
| tNanRequestReq nan_req; |
| VOS_STATUS status; |
| int ret_val = -EINVAL; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EPERM; |
| } |
| |
| if (!hdd_ctx->cfg_ini->enable_nan_support) { |
| hddLog(LOGE, FL("NaN is not suported")); |
| return -EPERM; |
| } |
| |
| nan_req.request_data_len = data_len; |
| nan_req.request_data = data; |
| |
| status = sme_NanRequest(&nan_req); |
| if (VOS_STATUS_SUCCESS != status) { |
| ret_val = -EINVAL; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_nan_request() - handle NAN request |
| * @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 send a NAN request to |
| * firmware. This is an SSR-protected wrapper function. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_nan_request(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_nan_request(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * FUNCTION: wlan_hdd_cfg80211_nan_callback |
| * This is a callback function and it gets called |
| * when we need to report nan response event to |
| * upper layers. |
| */ |
| static void wlan_hdd_cfg80211_nan_callback(void* ctx, tSirNanEvent* msg) |
| { |
| hdd_context_t *pHddCtx = (hdd_context_t *)ctx; |
| struct sk_buff *vendor_event; |
| int status; |
| tSirNanEvent *data; |
| |
| if (NULL == msg) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL(" msg received here is null")); |
| return; |
| } |
| data = msg; |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| |
| if (0 != status) |
| return; |
| |
| vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy, |
| NULL, |
| data->event_data_len + |
| NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_event_alloc failed")); |
| return; |
| } |
| if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NAN, |
| data->event_data_len, data->event_data)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR_NAN put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| } |
| |
| /* |
| * FUNCTION: wlan_hdd_cfg80211_nan_init |
| * This function is called to register the callback to sme layer |
| */ |
| void wlan_hdd_cfg80211_nan_init(hdd_context_t *pHddCtx) |
| { |
| sme_NanRegisterCallback(pHddCtx->hHal, wlan_hdd_cfg80211_nan_callback); |
| } |
| |
| #endif |
| |
| #ifdef WLAN_FEATURE_APFIND |
| /** |
| * __wlan_hdd_cfg80211_apfind_cmd() - set configuration 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 APFIND configurations to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int __wlan_hdd_cfg80211_apfind_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| struct sme_ap_find_request_req apfind_req; |
| VOS_STATUS status; |
| int ret_val; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EPERM; |
| } |
| |
| apfind_req.request_data_len = data_len; |
| apfind_req.request_data = data; |
| |
| status = sme_apfind_set_cmd(&apfind_req); |
| if (VOS_STATUS_SUCCESS != status) { |
| ret_val = -EIO; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_apfind_cmd() - set configuration 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 APFIND configurations to |
| * firmware. |
| * |
| * Return: An error code or 0 on success. |
| */ |
| static int wlan_hdd_cfg80211_apfind_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_apfind_cmd(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif /* WLAN_FEATURE_APFIND */ |
| |
| /* vendor specific events */ |
| static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = |
| { |
| #ifdef FEATURE_WLAN_CH_AVOID |
| [QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY |
| }, |
| #endif /* FEATURE_WLAN_CH_AVOID */ |
| |
| #ifdef WLAN_FEATURE_NAN |
| [QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN |
| }, |
| #endif |
| |
| #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 */ |
| /* EXT TDLS */ |
| [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 */ |
| /* OCB events */ |
| [QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT |
| }, |
| [QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI |
| }, |
| #ifdef WLAN_FEATURE_NAN_DATAPATH |
| [QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP |
| }, |
| #endif /* WLAN_FEATURE_NAN_DATAPATH */ |
| [QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX] = { |
| .vendor_id = QCA_NL80211_VENDOR_ID, |
| .subcmd = QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE |
| } |
| }; |
| |
| /** |
| * __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; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EPERM; |
| } |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) || \ |
| defined (DFS_MASTER_OFFLOAD_IND_SUPPORT) || defined(WITH_BACKPORTS) |
| 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 != NULL) |
| { |
| |
| ret_val = nla_put_u32(temp_skbuff, QCA_WLAN_VENDOR_ATTR_DFS, |
| dfs_capability); |
| if (ret_val) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: QCA_WLAN_VENDOR_ATTR_DFS put fail", __func__); |
| kfree_skb(temp_skbuff); |
| |
| return ret_val; |
| } |
| |
| return cfg80211_vendor_cmd_reply(temp_skbuff); |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: dfs capability: buffer alloc fail", __func__); |
| 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __is_driver_dfs_capable(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| static int |
| __wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct sk_buff *skb = NULL; |
| tANI_U32 fset = 0; |
| int ret; |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { |
| hddLog(LOG1, "Infra Station mode is supported by driver"); |
| fset |= WIFI_FEATURE_INFRA; |
| } |
| |
| if (TRUE == hdd_is_5g_supported(pHddCtx)) { |
| hddLog(LOG1, "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))) { |
| hddLog(LOG1, "WiFi-Direct is supported by driver"); |
| fset |= WIFI_FEATURE_P2P; |
| } |
| #endif |
| |
| /* Soft-AP is supported currently by default */ |
| fset |= WIFI_FEATURE_SOFT_AP; |
| |
| /* HOTSPOT is a supplicant feature, enable it by default */ |
| fset |= WIFI_FEATURE_HOTSPOT; |
| |
| #ifdef FEATURE_WLAN_EXTSCAN |
| if (pHddCtx->cfg_ini->extscan_enabled && |
| sme_IsFeatureSupportedByFW(EXTENDED_SCAN)) { |
| hddLog(LOG1, "EXTScan is supported by firmware"); |
| fset |= WIFI_FEATURE_EXTSCAN | WIFI_FEATURE_HAL_EPNO; |
| } |
| #endif |
| |
| #ifdef WLAN_FEATURE_NAN |
| if (sme_IsFeatureSupportedByFW(NAN)) { |
| hddLog(LOG1, "NAN is supported by firmware"); |
| fset |= WIFI_FEATURE_NAN; |
| } |
| #endif |
| |
| if (sme_IsFeatureSupportedByFW(RTT)) { |
| hddLog(LOG1, "RTT is supported by firmware"); |
| fset |= WIFI_FEATURE_D2D_RTT; |
| fset |= WIFI_FEATURE_D2AP_RTT; |
| } |
| |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| if (pHddCtx->cfg_ini->configPNOScanSupport && |
| sme_IsFeatureSupportedByFW(PNO)) { |
| hddLog(LOG1, "PNO is supported by firmware"); |
| fset |= WIFI_FEATURE_PNO; |
| } |
| #endif |
| |
| /* STA+STA is supported currently by default */ |
| fset |= WIFI_FEATURE_ADDITIONAL_STA; |
| |
| #ifdef FEATURE_WLAN_TDLS |
| if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSSupport) && |
| sme_IsFeatureSupportedByFW(TDLS)) { |
| hddLog(LOG1, "TDLS is supported by firmware"); |
| fset |= WIFI_FEATURE_TDLS; |
| } |
| |
| if (sme_IsFeatureSupportedByFW(TDLS) && |
| (TRUE == pHddCtx->cfg_ini->fEnableTDLSOffChannel) && |
| sme_IsFeatureSupportedByFW(TDLS_OFF_CHANNEL)) { |
| hddLog(LOG1, "TDLS off-channel is supported by firmware"); |
| fset |= WIFI_FEATURE_TDLS_OFFCHANNEL; |
| } |
| #endif |
| |
| #ifdef WLAN_AP_STA_CONCURRENCY |
| /* AP+STA concurrency is supported currently by default */ |
| fset |= WIFI_FEATURE_AP_STA; |
| #endif |
| fset |= WIFI_FEATURE_RSSI_MONITOR; |
| fset |= WIFI_FEATURE_TX_TRANSMIT_POWER; |
| |
| if (hdd_link_layer_stats_supported()) |
| fset |= WIFI_FEATURE_LINK_LAYER_STATS; |
| |
| if (hdd_roaming_supported(pHddCtx)) |
| fset |= WIFI_FEATURE_CONTROL_ROAMING; |
| |
| if (pHddCtx->cfg_ini->probe_req_ie_whitelist) |
| fset |= WIFI_FEATURE_IE_WHITELIST; |
| |
| if (hdd_scan_random_mac_addr_supported()) |
| fset |= WIFI_FEATURE_SCAN_RAND; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -EINVAL; |
| } |
| hddLog(LOG1, FL("Supported Features : 0x%x"), fset); |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_SET, fset)) { |
| hddLog(LOGE, FL("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) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_supported_features(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_fill_whitelist_ie_attrs - fill the white list members |
| * @ie_whitelist: enables whitelist |
| * @probe_req_ie_bitmap: bitmap to be filled |
| * @num_vendor_oui: pointer to no of ouis |
| * @voui: pointer to ouis to be filled |
| * @pHddCtx: pointer to hdd ctx |
| * |
| * This function fills the ie bitmap and vendor oui fields with the |
| * corresponding values present in cfg_ini and PHddCtx |
| * |
| * Return: Return none |
| */ |
| static void wlan_hdd_fill_whitelist_ie_attrs(bool *ie_whitelist, |
| uint32_t *probe_req_ie_bitmap, |
| uint32_t *num_vendor_oui, |
| struct vendor_oui *voui, |
| hdd_context_t *pHddCtx) |
| { |
| uint32_t i = 0; |
| |
| *ie_whitelist = true; |
| probe_req_ie_bitmap[0] = pHddCtx->cfg_ini->probe_req_ie_bitmap_0; |
| probe_req_ie_bitmap[1] = pHddCtx->cfg_ini->probe_req_ie_bitmap_1; |
| probe_req_ie_bitmap[2] = pHddCtx->cfg_ini->probe_req_ie_bitmap_2; |
| probe_req_ie_bitmap[3] = pHddCtx->cfg_ini->probe_req_ie_bitmap_3; |
| probe_req_ie_bitmap[4] = pHddCtx->cfg_ini->probe_req_ie_bitmap_4; |
| probe_req_ie_bitmap[5] = pHddCtx->cfg_ini->probe_req_ie_bitmap_5; |
| probe_req_ie_bitmap[6] = pHddCtx->cfg_ini->probe_req_ie_bitmap_6; |
| probe_req_ie_bitmap[7] = pHddCtx->cfg_ini->probe_req_ie_bitmap_7; |
| |
| *num_vendor_oui = 0; |
| |
| if ((pHddCtx->no_of_probe_req_ouis != 0) && (voui != NULL)) { |
| *num_vendor_oui = pHddCtx->no_of_probe_req_ouis; |
| for (i = 0; i < pHddCtx->no_of_probe_req_ouis; i++) { |
| voui[i].oui_type = pHddCtx->probe_req_voui[i].oui_type; |
| voui[i].oui_subtype = |
| pHddCtx->probe_req_voui[i].oui_subtype; |
| } |
| } |
| } |
| |
| /** |
| * __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) |
| { |
| tpSirScanMacOui pReqMsg = NULL; |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1]; |
| eHalStatus status; |
| int ret; |
| struct net_device *ndev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return ret; |
| |
| if (FALSE == pHddCtx->cfg_ini->enable_mac_spoofing) { |
| hddLog(LOGW, FL("MAC address spoofing is not enabled")); |
| return -ENOTSUPP; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX, |
| data, data_len, |
| NULL)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg) + |
| (pHddCtx->no_of_probe_req_ouis) * |
| (sizeof(struct vendor_oui))); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| vos_mem_zero(pReqMsg, sizeof(*pReqMsg) + |
| (pHddCtx->no_of_probe_req_ouis) * |
| (sizeof(struct vendor_oui))); |
| |
| /* Parse and fetch oui */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) { |
| hddLog(LOGE, FL("attr mac oui failed")); |
| goto fail; |
| } |
| |
| nla_memcpy(&pReqMsg->oui[0], |
| tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI], |
| sizeof(pReqMsg->oui)); |
| |
| /* populate pReqMsg for mac addr randomization */ |
| pReqMsg->vdev_id = pAdapter->sessionId; |
| pReqMsg->enb_probe_req_sno_randomization = 1; |
| |
| hddLog(LOG1, FL("Oui (%02x:%02x:%02x), vdev_id = %d"), pReqMsg->oui[0], |
| pReqMsg->oui[1], pReqMsg->oui[2], pReqMsg->vdev_id); |
| |
| if (pHddCtx->cfg_ini->probe_req_ie_whitelist) |
| wlan_hdd_fill_whitelist_ie_attrs(&pReqMsg->ie_whitelist, |
| pReqMsg->probe_req_ie_bitmap, |
| &pReqMsg->num_vendor_oui, |
| (struct vendor_oui *)((uint8_t *)pReqMsg + |
| sizeof(*pReqMsg)), |
| pHddCtx); |
| |
| status = sme_SetScanningMacOui(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_SetScanningMacOui failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| return 0; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_scanning_mac_oui(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #define MAX_CONCURRENT_MATRIX \ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX |
| #define MATRIX_CONFIG_PARAM_SET_SIZE_MAX \ |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX |
| static const struct nla_policy |
| wlan_hdd_get_concurrency_matrix_policy[MAX_CONCURRENT_MATRIX + 1] = { |
| [MATRIX_CONFIG_PARAM_SET_SIZE_MAX] = {.type = NLA_U32}, |
| }; |
| |
| static int |
| __wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| uint32_t feature_set_matrix[WLAN_HDD_MAX_FEATURE_SET] = {0}; |
| uint8_t i, feature_sets, max_feature_sets; |
| struct nlattr *tb[MAX_CONCURRENT_MATRIX + 1]; |
| struct sk_buff *reply_skb; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| int ret; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (nla_parse(tb, MAX_CONCURRENT_MATRIX, |
| data, data_len, wlan_hdd_get_concurrency_matrix_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch max feature set */ |
| if (!tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]) { |
| hddLog(LOGE, FL("Attr max feature set size failed")); |
| return -EINVAL; |
| } |
| |
| max_feature_sets = nla_get_u32(tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]); |
| hddLog(LOG1, FL("Max feature set size: %d"), max_feature_sets); |
| |
| /* Fill feature combination matrix */ |
| feature_sets = 0; |
| feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA | |
| WIFI_FEATURE_P2P; |
| feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA | |
| WIFI_FEATURE_NAN; |
| |
| /* Add more feature combinations here */ |
| |
| feature_sets = VOS_MIN(feature_sets, max_feature_sets); |
| hddLog(LOG1, FL("Number of feature sets: %d"), feature_sets); |
| hddLog(LOG1, "Feature set matrix"); |
| for (i = 0; i < feature_sets; i++) |
| hddLog(LOG1, "[%d] 0x%02X", i, feature_set_matrix[i]); |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + |
| sizeof(u32) * feature_sets + |
| NLMSG_HDRLEN); |
| |
| if (reply_skb) { |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE, |
| feature_sets) || |
| nla_put(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET, |
| sizeof(u32) * feature_sets, feature_set_matrix)) { |
| hddLog(LOGE, FL("nla put fail")); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| ret = cfg80211_vendor_cmd_reply(reply_skb); |
| EXIT(); |
| return ret; |
| } |
| hddLog(LOGE, FL("Feature set matrix: buffer alloc fail")); |
| return -ENOMEM; |
| } |
| |
| #undef MAX_CONCURRENT_MATRIX |
| #undef MATRIX_CONFIG_PARAM_SET_SIZE_MAX |
| |
| /** |
| * wlan_hdd_cfg80211_get_concurrency_matrix() - get concurrency matrix |
| * @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_concurrency_matrix(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_concurrency_matrix(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| return ret; |
| } |
| |
| #define MAX_ROAMING_PARAM \ |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX |
| |
| static 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}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE] = { |
| .type = NLA_S32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID] = { |
| .type = NLA_BINARY, |
| .len = MAC_ADDRESS_STR_LEN}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID] = { |
| .type = NLA_BINARY, |
| .len = MAC_ADDRESS_STR_LEN}, |
| }; |
| |
| 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; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| uint8_t session_id; |
| struct roam_ext_params roam_params; |
| uint32_t cmd_type, req_id; |
| struct nlattr *curr_attr = NULL; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX + 1]; |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX + 1]; |
| int rem, i; |
| uint32_t buf_len = 0; |
| uint32_t count; |
| int ret; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_set_roam_param_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| /* Parse and fetch Command Type*/ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]) { |
| hddLog(LOGE, FL("roam cmd type failed")); |
| goto fail; |
| } |
| session_id = pAdapter->sessionId; |
| vos_mem_set(&roam_params, sizeof(roam_params),0); |
| cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]); |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| req_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Req Id (%d)"), req_id); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Cmd Type (%d)"), cmd_type); |
| switch(cmd_type) { |
| case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST: |
| i = 0; |
| if (tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]) { |
| count = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]); |
| } else { |
| hddLog(LOGE, FL("Number of networks is not provided")); |
| goto fail; |
| } |
| |
| if (count && |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST]) { |
| nla_for_each_nested(curr_attr, |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST], |
| rem) { |
| if (nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX, |
| nla_data(curr_attr), nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| goto fail; |
| } |
| /* Parse and Fetch allowed SSID list*/ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]) { |
| hddLog(LOGE, FL("attr allowed ssid failed")); |
| goto fail; |
| } |
| buf_len = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_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 && (i < MAX_SSID_ALLOWED_LIST) && |
| ((buf_len - 1) <= SIR_MAC_MAX_SSID_LENGTH)) { |
| nla_memcpy(roam_params.ssid_allowed_list[i].ssId, |
| tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID], |
| buf_len - 1); |
| roam_params.ssid_allowed_list[i].length = |
| buf_len - 1; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("SSID[%d]: %.*s,length = %d"), i, |
| roam_params.ssid_allowed_list[i].length, |
| roam_params.ssid_allowed_list[i].ssId, |
| roam_params.ssid_allowed_list[i].length); |
| i++; |
| } else { |
| hddLog(LOGE, FL("Invalid SSID len %d,idx %d"), |
| buf_len, i); |
| } |
| } |
| } |
| if (i != count) { |
| hddLog(LOGE, FL("Invalid number of SSIDs i = %d, count = %d"), |
| i, count); |
| goto fail; |
| } |
| roam_params.num_ssid_allowed_list = i; |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Num of Allowed SSID %d"), |
| roam_params.num_ssid_allowed_list); |
| sme_update_roam_params(pHddCtx->hHal, session_id, |
| roam_params, REASON_ROAM_SET_SSID_ALLOWED); |
| break; |
| case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_EXTSCAN_ROAM_PARAMS: |
| /* Parse and fetch 5G Boost Threshold */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD]) { |
| hddLog(LOGE, FL("5G boost threshold failed")); |
| goto fail; |
| } |
| roam_params.raise_rssi_thresh_5g = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("5G Boost Threshold (%d)"), |
| roam_params.raise_rssi_thresh_5g); |
| /* Parse and fetch 5G Penalty Threshold */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD]) { |
| hddLog(LOGE, FL("5G penalty threshold failed")); |
| goto fail; |
| } |
| roam_params.drop_rssi_thresh_5g = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("5G Penalty Threshold (%d)"), |
| roam_params.drop_rssi_thresh_5g); |
| /* Parse and fetch 5G Boost Factor */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR]) { |
| hddLog(LOGE, FL("5G boost Factor failed")); |
| goto fail; |
| } |
| roam_params.raise_factor_5g = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("5G Boost Factor (%d)"), |
| roam_params.raise_factor_5g); |
| /* Parse and fetch 5G Penalty factor */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR]) { |
| hddLog(LOGE, FL("5G Penalty Factor failed")); |
| goto fail; |
| } |
| roam_params.drop_factor_5g = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("5G Penalty factor (%d)"), |
| roam_params.drop_factor_5g); |
| /* Parse and fetch 5G Max Boost */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST]) { |
| hddLog(LOGE, FL("5G Max Boost failed")); |
| goto fail; |
| } |
| roam_params.max_raise_rssi_5g = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("5G Max Boost (%d)"), |
| roam_params.max_raise_rssi_5g); |
| /* Parse and fetch Rssi Diff */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS]) { |
| hddLog(LOGE, FL("Rssi Diff failed")); |
| goto fail; |
| } |
| roam_params.rssi_diff = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("RSSI Diff (%d)"), |
| roam_params.rssi_diff); |
| /* Parse and fetch Good Rssi Threshold */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER]) { |
| hddLog(LOGE, FL("Alert Rssi Threshold failed")); |
| goto fail; |
| } |
| roam_params.alert_rssi_threshold = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("Alert RSSI Threshold (%d)"), |
| roam_params.alert_rssi_threshold); |
| sme_update_roam_params(pHddCtx->hHal, session_id, |
| roam_params, |
| REASON_ROAM_EXT_SCAN_PARAMS_CHANGED); |
| break; |
| case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM: |
| /* Parse and fetch Activate Good Rssi Roam */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE]) { |
| hddLog(LOGE, FL("Activate Good Rssi Roam failed")); |
| goto fail; |
| } |
| roam_params.good_rssi_roam = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("Activate Good Rssi Roam (%d)"), |
| roam_params.good_rssi_roam); |
| sme_update_roam_params(pHddCtx->hHal, session_id, |
| roam_params, REASON_ROAM_GOOD_RSSI_CHANGED); |
| break; |
| case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS: |
| /* Parse and fetch number of preferred BSSID */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID]) { |
| hddLog(LOGE, FL("attr num of preferred bssid failed")); |
| goto fail; |
| } |
| count = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID]); |
| if (count > MAX_BSSID_FAVORED) { |
| hddLog(LOGE, FL("Preferred BSSID count %u exceeds max %u"), |
| count, MAX_BSSID_FAVORED); |
| goto fail; |
| } |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("Num of Preferred BSSID: %d"), count); |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS]) { |
| hddLog(LOGE, FL("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) { |
| hddLog(LOGW, FL("Ignoring excess Preferred BSSID")); |
| break; |
| } |
| |
| if (nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| nla_data(curr_attr), nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| goto fail; |
| } |
| /* Parse and fetch MAC address */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID]) { |
| hddLog(LOGE, FL("attr mac address failed")); |
| goto fail; |
| } |
| nla_memcpy(roam_params.bssid_favored[i], |
| tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID], |
| sizeof(tSirMacAddr)); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(roam_params.bssid_favored[i])); |
| /* Parse and fetch preference factor*/ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER]) { |
| hddLog(LOGE, FL("BSSID Preference score failed")); |
| goto fail; |
| } |
| roam_params.bssid_favored_factor[i] = nla_get_u32( |
| tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER]); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("BSSID Preference score (%d)"), |
| roam_params.bssid_favored_factor[i]); |
| i++; |
| } |
| if (i < count) |
| hddLog(LOGW, |
| FL("Num Preferred BSSID %u less than expected %u"), |
| i, count); |
| roam_params.num_bssid_favored = i; |
| sme_update_roam_params(pHddCtx->hHal, session_id, |
| roam_params, REASON_ROAM_SET_FAVORED_BSSID); |
| break; |
| case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID: |
| /* Parse and fetch number of blacklist BSSID */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID]) { |
| hddLog(LOGE, FL("attr num of blacklist bssid failed")); |
| goto fail; |
| } |
| count = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID]); |
| if (count > MAX_BSSID_AVOID_LIST) { |
| hddLog(LOGE, FL("Blacklist BSSID count %u exceeds max %u"), |
| count, MAX_BSSID_AVOID_LIST); |
| goto fail; |
| } |
| hddLog(VOS_TRACE_LEVEL_DEBUG, |
| FL("Num of blacklist BSSID: %d"), count); |
| i = 0; |
| if (count && |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS]) { |
| nla_for_each_nested(curr_attr, |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS], |
| rem) { |
| if (i == count) { |
| hddLog(LOGW, FL("Ignoring excess Blacklist BSSID")); |
| break; |
| } |
| if (nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX, |
| nla_data(curr_attr), nla_len(curr_attr), |
| wlan_hdd_set_roam_param_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| goto fail; |
| } |
| /* Parse and fetch MAC address */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID]) { |
| hddLog(LOGE, FL("attr blacklist addr failed")); |
| goto fail; |
| } |
| nla_memcpy(roam_params.bssid_avoid_list[i], |
| tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID], |
| sizeof(tSirMacAddr)); |
| hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY( |
| roam_params.bssid_avoid_list[i])); |
| i++; |
| } |
| } |
| if (i < count) |
| hddLog(LOGW, |
| FL("Num Blacklist BSSID %u less than expected %u"), |
| i, count); |
| roam_params.num_bssid_avoid_list = i; |
| sme_update_roam_params(pHddCtx->hHal, session_id, |
| roam_params, REASON_ROAM_SET_BLACKLIST_BSSID); |
| break; |
| } |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_ext_roam_params(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #ifdef WLAN_FEATURE_STATS_EXT |
| /** |
| * __wlan_hdd_cfg80211_stats_ext_request() - ext stats request |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: int |
| */ |
| static int __wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tStatsExtRequestReq stats_ext_req; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| int ret_val; |
| eHalStatus status; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| stats_ext_req.request_data_len = data_len; |
| stats_ext_req.request_data = (void *)data; |
| |
| status = sme_StatsExtRequest(pAdapter->sessionId, &stats_ext_req); |
| |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EINVAL; |
| |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_stats_ext_request() - ext stats request |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: int |
| */ |
| static int wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_stats_ext_request(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static void wlan_hdd_cfg80211_stats_ext_callback(void* ctx, tStatsExtEvent* msg) |
| { |
| |
| hdd_context_t *pHddCtx = (hdd_context_t *)ctx; |
| struct sk_buff *vendor_event; |
| int status; |
| int ret_val; |
| tStatsExtEvent *data = msg; |
| hdd_adapter_t *pAdapter = NULL; |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| |
| if (0 != status) |
| return; |
| |
| pAdapter = hdd_get_adapter_by_vdev( pHddCtx, data->vdev_id); |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: vdev_id %d does not exist with host", |
| __func__, data->vdev_id); |
| return; |
| } |
| |
| |
| vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy, |
| NULL, |
| data->event_data_len + |
| sizeof(tANI_U32) + |
| NLMSG_HDRLEN + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: cfg80211_vendor_event_alloc failed", __func__); |
| return; |
| } |
| |
| ret_val = nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_IFINDEX, |
| pAdapter->dev->ifindex); |
| if (ret_val) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: QCA_WLAN_VENDOR_ATTR_IFINDEX put fail", __func__); |
| kfree_skb(vendor_event); |
| |
| return; |
| } |
| |
| |
| ret_val = nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_STATS_EXT, |
| data->event_data_len, data->event_data); |
| |
| if (ret_val) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: QCA_WLAN_VENDOR_ATTR_STATS_EXT put fail", __func__); |
| kfree_skb(vendor_event); |
| |
| return; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_stats_ext2_callback - stats_ext2_callback |
| * @ctx: hdd context |
| * @pmsg: stats_ext2_event |
| * |
| * Return: void |
| */ |
| static void wlan_hdd_cfg80211_stats_ext2_callback(void *ctx, |
| struct stats_ext2_event *pmsg) |
| { |
| hdd_context_t *hdd_ctx = (hdd_context_t *)ctx; |
| int status, data_size; |
| struct sk_buff *vendor_event; |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return; |
| |
| if (NULL == pmsg) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "msg received here is null"); |
| return; |
| } |
| |
| data_size = sizeof(struct stats_ext2_event) + |
| (pmsg->hole_cnt)*sizeof(pmsg->hole_info_array[0]); |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, |
| data_size + NLMSG_HDRLEN + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "vendor_event_alloc failed for STATS_EXT2"); |
| return; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM, |
| pmsg->hole_cnt)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s put fail", |
| "QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO, |
| (pmsg->hole_cnt)*sizeof(pmsg->hole_info_array[0]), |
| (void *)(pmsg->hole_info_array))) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s put fail", |
| "QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO"); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| } |
| |
| void wlan_hdd_cfg80211_stats_ext_init(hdd_context_t *pHddCtx) |
| { |
| sme_StatsExtRegisterCallback(pHddCtx->hHal, |
| wlan_hdd_cfg80211_stats_ext_callback); |
| sme_register_stats_ext2_callback(pHddCtx->hHal, |
| wlan_hdd_cfg80211_stats_ext2_callback); |
| } |
| |
| #endif |
| |
| #ifdef FEATURE_WLAN_EXTSCAN |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_send_ext_scan_capability() |
| */ |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_STATUS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS |
| #define MAX_SCAN_CACHE_SIZE \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE |
| #define MAX_SCAN_BUCKETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS |
| #define MAX_AP_CACHE_PER_SCAN \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN |
| #define MAX_RSSI_SAMPLE_SIZE \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE |
| #define MAX_SCAN_RPT_THRHOLD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD |
| #define MAX_HOTLIST_BSSIDS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS |
| #define MAX_SIGNIFICANT_WIFI_CHANGE_APS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS |
| #define MAX_BSSID_HISTORY_ENTRIES \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES |
| #define MAX_HOTLIST_SSIDS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS |
| #define MAX_NUM_EPNO_NETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS |
| #define MAX_NUM_EPNO_NETS_BY_SSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID |
| #define MAX_NUM_WHITELISTED_SSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID |
| #define MAX_NUM_BLACKLISTED_BSSID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID |
| |
| /** |
| * wlan_hdd_send_ext_scan_capability - send ext scan capability to user space |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_send_ext_scan_capability(hdd_context_t *hdd_ctx) |
| { |
| int ret; |
| struct sk_buff *skb; |
| struct ext_scan_capabilities_response *data; |
| uint32_t nl_buf_len; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| data = &(hdd_ctx->ext_scan_context.capability_response); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) + |
| (sizeof(data->status) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_cache_size) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_buckets) + NLA_HDRLEN) + |
| (sizeof(data->max_ap_cache_per_scan) + NLA_HDRLEN) + |
| (sizeof(data->max_rssi_sample_size) + NLA_HDRLEN) + |
| (sizeof(data->max_scan_reporting_threshold) + NLA_HDRLEN) + |
| (sizeof(data->max_hotlist_bssids) + NLA_HDRLEN) + |
| (sizeof(data->max_significant_wifi_change_aps) + NLA_HDRLEN) + |
| (sizeof(data->max_bssid_history_entries) + NLA_HDRLEN) + |
| (sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) + |
| (sizeof(data->max_number_epno_networks) + NLA_HDRLEN) + |
| (sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) + |
| (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) + |
| (sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| hddLog(LOG1, "Req Id (%u)", data->requestId); |
| hddLog(LOG1, "Status (%u)", data->status); |
| hddLog(LOG1, "Scan cache size (%u)", data->max_scan_cache_size); |
| hddLog(LOG1, "Scan buckets (%u)", data->max_scan_buckets); |
| hddLog(LOG1, "Max AP per scan (%u)", data->max_ap_cache_per_scan); |
| hddLog(LOG1, "max_rssi_sample_size (%u)", |
| data->max_rssi_sample_size); |
| hddLog(LOG1, "max_scan_reporting_threshold (%u)", |
| data->max_scan_reporting_threshold); |
| hddLog(LOG1, "max_hotlist_bssids (%u)", data->max_hotlist_bssids); |
| hddLog(LOG1, "max_significant_wifi_change_aps (%u)", |
| data->max_significant_wifi_change_aps); |
| hddLog(LOG1, "max_bssid_history_entries (%u)", |
| data->max_bssid_history_entries); |
| hddLog(LOG1, "max_hotlist_ssids (%u)", data->max_hotlist_ssids); |
| hddLog(LOG1, "max_number_epno_networks (%u)", |
| data->max_number_epno_networks); |
| hddLog(LOG1, "max_number_epno_networks_by_ssid (%u)", |
| data->max_number_epno_networks_by_ssid); |
| hddLog(LOG1, "max_number_of_white_listed_ssid (%u)", |
| data->max_number_of_white_listed_ssid); |
| hddLog(LOG1, "max_number_of_black_listed_bssid (%u)", |
| data->max_number_of_black_listed_bssid); |
| |
| if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) || |
| nla_put_u32(skb, PARAM_STATUS, data->status) || |
| nla_put_u32(skb, MAX_SCAN_CACHE_SIZE, data->max_scan_cache_size) || |
| nla_put_u32(skb, MAX_SCAN_BUCKETS, data->max_scan_buckets) || |
| nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN, |
| data->max_ap_cache_per_scan) || |
| nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE, |
| data->max_rssi_sample_size) || |
| nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD, |
| data->max_scan_reporting_threshold) || |
| nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->max_hotlist_bssids) || |
| nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS, |
| data->max_significant_wifi_change_aps) || |
| nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES, |
| data->max_bssid_history_entries) || |
| nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->max_hotlist_ssids) || |
| nla_put_u32(skb, MAX_NUM_EPNO_NETS, |
| data->max_number_epno_networks) || |
| nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID, |
| data->max_number_epno_networks_by_ssid) || |
| nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID, |
| data->max_number_of_white_listed_ssid) || |
| nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID, |
| data->max_number_of_black_listed_bssid)) { |
| hddLog(LOGE, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_send_ext_scan_capability() |
| */ |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_STATUS |
| #undef MAX_SCAN_CACHE_SIZE |
| #undef MAX_SCAN_BUCKETS |
| #undef MAX_AP_CACHE_PER_SCAN |
| #undef MAX_RSSI_SAMPLE_SIZE |
| #undef MAX_SCAN_RPT_THRHOLD |
| #undef MAX_HOTLIST_BSSIDS |
| #undef MAX_SIGNIFICANT_WIFI_CHANGE_APS |
| #undef MAX_BSSID_HISTORY_ENTRIES |
| #undef MAX_HOTLIST_SSIDS |
| #undef MAX_NUM_EPNO_NETS |
| #undef MAX_NUM_EPNO_NETS_BY_SSID |
| #undef MAX_NUM_WHITELISTED_SSID |
| #undef MAX_NUM_BLACKLISTED_BSSID |
| |
| static int __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| unsigned long rc; |
| struct hdd_ext_scan_context *context; |
| tpSirGetExtScanCapabilitiesReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| eHalStatus status; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id %d"), pReqMsg->requestId); |
| |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id %d"), pReqMsg->sessionId); |
| |
| spin_lock(&hdd_context_lock); |
| context = &pHddCtx->ext_scan_context; |
| context->request_id = pReqMsg->requestId; |
| INIT_COMPLETION(context->response_event); |
| spin_unlock(&hdd_context_lock); |
| |
| |
| status = sme_ExtScanGetCapabilities(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_ExtScanGetCapabilities failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hddLog(LOGE, FL("Target response timed out")); |
| return -ETIMEDOUT; |
| } |
| |
| ret = wlan_hdd_send_ext_scan_capability(pHddCtx); |
| if (ret) |
| hddLog(LOGE, FL("Failed to send ext scan capability to user space")); |
| |
| EXIT(); |
| return ret; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_get_cached_results() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_FLUSH \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH |
| /** |
| * __wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results |
| * @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. |
| * Each WMI event with cached scan results data chunk results in |
| * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each |
| * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. |
| * |
| * If timeout happens before receiving all of the data, this function sets |
| * a context variable @ignore_cached_results to %true, all of the next data |
| * chunks are checked against this variable and dropped. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int __wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanGetCachedResultsReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct hdd_ext_scan_context *context; |
| struct nlattr *tb[PARAM_MAX + 1]; |
| eHalStatus status; |
| int retval = 0; |
| unsigned long rc; |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = pAdapter->sessionId; |
| |
| /* Parse and fetch flush parameter */ |
| if (!tb[PARAM_FLUSH]) { |
| hddLog(LOGE, FL("attr flush failed")); |
| goto fail; |
| } |
| pReqMsg->flush = nla_get_u8(tb[PARAM_FLUSH]); |
| hddLog(LOG1, FL("Req Id: %u Session Id: %d Flush: %d"), |
| pReqMsg->requestId, pReqMsg->sessionId, pReqMsg->flush); |
| |
| spin_lock(&hdd_context_lock); |
| context = &pHddCtx->ext_scan_context; |
| context->request_id = pReqMsg->requestId; |
| context->ignore_cached_results = false; |
| INIT_COMPLETION(context->response_event); |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_getCachedResults(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_getCachedResults failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hddLog(LOGE, FL("Target response timed out")); |
| retval = -ETIMEDOUT; |
| spin_lock(&hdd_context_lock); |
| context->ignore_cached_results = true; |
| spin_unlock(&hdd_context_lock); |
| } else { |
| spin_lock(&hdd_context_lock); |
| retval = context->response_status; |
| spin_unlock(&hdd_context_lock); |
| } |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_get_cached_results() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_FLUSH |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results |
| * @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. |
| * Each WMI event with cached scan results data chunk results in |
| * function call wlan_hdd_cfg80211_extscan_cached_results_ind and each |
| * data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb. |
| * |
| * If timeout happens before receiving all of the data, this function sets |
| * a context variable @ignore_cached_results to %true, all of the next data |
| * chunks are checked against this variable and dropped. |
| * |
| * Return: 0 on success; error number otherwise. |
| */ |
| static int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| static int __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanSetBssidHotListReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *apTh; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id; |
| eHalStatus status; |
| tANI_U8 i; |
| int rem, retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| hddLog(LOG1, FL("Req Id %d"), pReqMsg->requestId); |
| |
| /* Parse and fetch number of APs */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]) { |
| hddLog(LOGE, FL("attr number of AP failed")); |
| goto fail; |
| } |
| pReqMsg->numAp = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]); |
| if (pReqMsg->numAp > WLAN_EXTSCAN_MAX_HOTLIST_APS) { |
| hddLog(LOGE, FL("Number of AP: %u exceeds max: %u"), |
| pReqMsg->numAp, WLAN_EXTSCAN_MAX_HOTLIST_APS); |
| goto fail; |
| } |
| hddLog(LOG1, FL("Number of AP %d"), pReqMsg->numAp); |
| |
| /* Parse and fetch lost ap sample size */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]) { |
| hddLog(LOGE, FL("attr lost ap sample size failed")); |
| goto fail; |
| } |
| |
| pReqMsg->lost_ap_sample_size = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]); |
| hddLog(LOG1, FL("Lost ap sample size %d"), pReqMsg->lost_ap_sample_size); |
| |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(LOG1, FL("Session Id %d"), pReqMsg->sessionId); |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM]) { |
| hddLog(LOGE, FL("attr ap threshold failed")); |
| goto fail; |
| } |
| i = 0; |
| nla_for_each_nested(apTh, |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM], rem) { |
| if (i == pReqMsg->numAp) { |
| hddLog(LOGW, FL("Ignoring excess AP")); |
| break; |
| } |
| |
| if (nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(apTh), nla_len(apTh), |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| goto fail; |
| } |
| |
| /* Parse and fetch MAC address */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]) { |
| hddLog(LOGE, FL("attr mac address failed")); |
| goto fail; |
| } |
| nla_memcpy(pReqMsg->ap[i].bssid, |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID], |
| sizeof(tSirMacAddr)); |
| hddLog(LOG1, MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(pReqMsg->ap[i].bssid)); |
| |
| /* Parse and fetch low RSSI */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]) { |
| hddLog(LOGE, FL("attr low RSSI failed")); |
| goto fail; |
| } |
| pReqMsg->ap[i].low = nla_get_s32( |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]); |
| hddLog(LOG1, FL("RSSI low %d"), pReqMsg->ap[i].low); |
| |
| /* Parse and fetch high RSSI */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]) { |
| hddLog(LOGE, FL("attr high RSSI failed")); |
| goto fail; |
| } |
| pReqMsg->ap[i].high = nla_get_s32( |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]); |
| hddLog(LOG1, FL("RSSI High %d"), pReqMsg->ap[i].high); |
| i++; |
| } |
| |
| if (i < pReqMsg->numAp) { |
| hddLog(LOGW, FL("Number of AP %u less than expected %u"), |
| i, pReqMsg->numAp); |
| pReqMsg->numAp = i; |
| } |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_SetBssHotlist(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_SetBssHotlist failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("sme_SetBssHotlist timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set ext scan bssid hotlist |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| static int __wlan_hdd_cfg80211_extscan_set_significant_change( |
| struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanSetSigChangeReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *apTh; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id; |
| eHalStatus status; |
| tANI_U8 i; |
| int rem; |
| int retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| hddLog(LOG1, FL("Req Id %d"), pReqMsg->requestId); |
| |
| /* Parse and fetch RSSI sample size */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE]) |
| { |
| hddLog(LOGE, FL("attr RSSI sample size failed")); |
| goto fail; |
| } |
| pReqMsg->rssiSampleSize = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE]); |
| hddLog(LOG1, FL("RSSI sample size %u"), pReqMsg->rssiSampleSize); |
| |
| /* Parse and fetch lost AP sample size */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE]) |
| { |
| hddLog(LOGE, FL("attr lost AP sample size failed")); |
| goto fail; |
| } |
| pReqMsg->lostApSampleSize = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE]); |
| hddLog(LOG1, FL("Lost AP sample size %u"), pReqMsg->lostApSampleSize); |
| |
| /* Parse and fetch AP min breaching */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING]) |
| { |
| hddLog(LOGE, FL("attr AP min breaching")); |
| goto fail; |
| } |
| pReqMsg->minBreaching = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING]); |
| hddLog(LOG1, FL("AP min breaching %u"), pReqMsg->minBreaching); |
| |
| /* Parse and fetch number of APs */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP]) { |
| hddLog(LOGE, FL("attr number of AP failed")); |
| goto fail; |
| } |
| pReqMsg->numAp = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP]); |
| if (pReqMsg->numAp > WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS) { |
| hddLog(LOGE, FL("Number of AP %u exceeds max %u"), |
| pReqMsg->numAp, WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS); |
| goto fail; |
| } |
| |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(LOG1, FL("Number of AP %d Session Id %d"), pReqMsg->numAp, |
| pReqMsg->sessionId); |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM]) { |
| hddLog(LOGE, FL("attr ap threshold failed")); |
| goto fail; |
| } |
| i = 0; |
| nla_for_each_nested(apTh, |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM], rem) { |
| |
| if (i == pReqMsg->numAp) { |
| hddLog(LOGW, FL("Ignoring excess AP")); |
| break; |
| } |
| |
| if (nla_parse(tb2, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(apTh), nla_len(apTh), |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| goto fail; |
| } |
| |
| /* Parse and fetch MAC address */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]) { |
| hddLog(LOGE, FL("attr mac address failed")); |
| goto fail; |
| } |
| nla_memcpy(pReqMsg->ap[i].bssid, |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID], |
| sizeof(tSirMacAddr)); |
| hddLog(LOG1, MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(pReqMsg->ap[i].bssid)); |
| |
| /* Parse and fetch low RSSI */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]) { |
| hddLog(LOGE, FL("attr low RSSI failed")); |
| goto fail; |
| } |
| pReqMsg->ap[i].low = nla_get_s32( |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]); |
| hddLog(LOG1, FL("RSSI low %d"), pReqMsg->ap[i].low); |
| |
| /* Parse and fetch high RSSI */ |
| if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]) { |
| hddLog(LOGE, FL("attr high RSSI failed")); |
| goto fail; |
| } |
| pReqMsg->ap[i].high = nla_get_s32( |
| tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]); |
| hddLog(LOG1, FL("RSSI High %d"), pReqMsg->ap[i].high); |
| |
| i++; |
| } |
| if (i < pReqMsg->numAp) { |
| hddLog(LOGW, FL("Number of AP %u less than expected %u"), |
| i, pReqMsg->numAp); |
| pReqMsg->numAp = i; |
| } |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_SetSignificantChange(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_SetSignificantChange failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("sme_SetSignificantChange timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change |
| * @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_set_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_set_significant_change(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static int __wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| uint32_t chan_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| uint8_t num_channels = 0, num_chan_new = 0, buf[256] = {0}; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| tANI_U32 requestId, maxChannels; |
| tWifiBand wifiBand; |
| eHalStatus status; |
| struct sk_buff *reply_skb; |
| int i, j, k, retval, len = 0; |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| return -EINVAL; |
| } |
| requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| /* Parse and fetch wifi band */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]) { |
| hddLog(LOGE, FL("attr wifi band failed")); |
| return -EINVAL; |
| } |
| wifiBand = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]); |
| |
| /* Parse and fetch max channels */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]) { |
| hddLog(LOGE, FL("attr max channels failed")); |
| return -EINVAL; |
| } |
| maxChannels = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]); |
| hddLog(LOG1, FL("Req Id: %d Wifi band: %d Max channels: %d"), |
| requestId, wifiBand, maxChannels); |
| |
| status = sme_GetValidChannelsByBand((tHalHandle)(pHddCtx->hHal), |
| wifiBand, chan_list, |
| &num_channels); |
| if (eHAL_STATUS_SUCCESS != status) { |
| hddLog(LOGE, |
| FL("sme_GetValidChannelsByBand failed (err=%d)"), status); |
| return -EINVAL; |
| } |
| |
| num_channels = VOS_MIN(num_channels, maxChannels); |
| |
| /* remove the DSRC channels from the list */ |
| num_chan_new = 0; |
| for (i = 0; i < num_channels; i++) { |
| if (!vos_is_dsrc_channel(chan_list[i])) { |
| chan_list[num_chan_new] = chan_list[i]; |
| num_chan_new++; |
| } |
| } |
| |
| num_channels = num_chan_new; |
| |
| /* remove the indoor only channels if iface is SAP */ |
| if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) || |
| !strncmp(hdd_get_fwpath(), "ap", 2)) { |
| num_chan_new = 0; |
| for (i = 0; i < num_channels; i++) |
| for (j = 0; j < IEEE80211_NUM_BANDS; j++) { |
| if (wiphy->bands[j] == NULL) |
| 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_INDOOR_ONLY))) { |
| chan_list[num_chan_new] = chan_list[i]; |
| num_chan_new++; |
| } |
| } |
| } |
| } |
| |
| hddLog(LOG1, FL("Number of channels: %d"), num_chan_new); |
| for (i = 0; i < num_chan_new; i++) |
| len += scnprintf(buf + len, sizeof(buf) - len, "%u ", chan_list[i]); |
| hddLog(LOG1, "Channels: %s", buf); |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) + |
| sizeof(u32) * num_chan_new + |
| NLMSG_HDRLEN); |
| |
| if (reply_skb) { |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS, |
| num_chan_new) || |
| nla_put(reply_skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS, |
| sizeof(u32) * num_chan_new, chan_list)) { |
| hddLog(LOGE, FL("nla put fail")); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| retval = cfg80211_vendor_cmd_reply(reply_skb); |
| return retval; |
| } |
| hddLog(LOGE, FL("valid channels: buffer alloc fail")); |
| return -EINVAL; |
| } |
| |
| /** |
| * 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) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_extscan_update_dwell_time_limits() - update dwell times |
| * @req_msg: Pointer to request message |
| * @bkt_idx: Index of current bucket being processed |
| * @active_min: minimum active dwell time |
| * @active_max: maximum active dwell time |
| * @passive_min: minimum passive dwell time |
| * @passive_max: maximum passive dwell time |
| * |
| * Return: none |
| */ |
| static void hdd_extscan_update_dwell_time_limits( |
| tpSirWifiScanCmdReqParams req_msg, uint32_t bkt_idx, |
| uint32_t active_min, uint32_t active_max, |
| uint32_t passive_min, uint32_t passive_max) |
| { |
| /* update per-bucket dwell times */ |
| if (req_msg->buckets[bkt_idx].min_dwell_time_active > |
| active_min) { |
| req_msg->buckets[bkt_idx].min_dwell_time_active = |
| active_min; |
| } |
| if (req_msg->buckets[bkt_idx].max_dwell_time_active < |
| active_max) { |
| req_msg->buckets[bkt_idx].max_dwell_time_active = |
| active_max; |
| } |
| if (req_msg->buckets[bkt_idx].min_dwell_time_passive > |
| passive_min) { |
| req_msg->buckets[bkt_idx].min_dwell_time_passive = |
| passive_min; |
| } |
| if (req_msg->buckets[bkt_idx].max_dwell_time_passive < |
| passive_max) { |
| req_msg->buckets[bkt_idx].max_dwell_time_passive = |
| passive_max; |
| } |
| /* update dwell-time across all buckets */ |
| if (req_msg->min_dwell_time_active > |
| req_msg->buckets[bkt_idx].min_dwell_time_active) { |
| req_msg->min_dwell_time_active = |
| req_msg->buckets[bkt_idx].min_dwell_time_active; |
| } |
| if (req_msg->max_dwell_time_active < |
| req_msg->buckets[bkt_idx].max_dwell_time_active) { |
| req_msg->max_dwell_time_active = |
| req_msg->buckets[bkt_idx].max_dwell_time_active; |
| } |
| if (req_msg->min_dwell_time_passive > |
| req_msg->buckets[bkt_idx].min_dwell_time_passive) { |
| req_msg->min_dwell_time_passive = |
| req_msg->buckets[bkt_idx].min_dwell_time_passive; |
| } |
| if (req_msg->max_dwell_time_passive > |
| req_msg->buckets[bkt_idx].max_dwell_time_passive) { |
| req_msg->max_dwell_time_passive = |
| req_msg->buckets[bkt_idx].max_dwell_time_passive; |
| } |
| } |
| |
| /** |
| * hdd_extscan_channel_max_reached() - channel max reached |
| * @req: extscan request structure |
| * @total_channels: total number of channels |
| * |
| * Return: true if total channels reached max, false otherwise |
| */ |
| static bool hdd_extscan_channel_max_reached(tSirWifiScanCmdReqParams *req, |
| uint8_t total_channels) |
| { |
| if (total_channels == WLAN_EXTSCAN_MAX_CHANNELS) { |
| hddLog(LOGW, |
| FL("max #of channels %d reached, taking only first %d bucket(s)"), |
| total_channels, req->numBuckets); |
| return true; |
| } |
| return false; |
| } |
| |
| static int hdd_extscan_start_fill_bucket_channel_spec( |
| hdd_context_t *pHddCtx, |
| tpSirWifiScanCmdReqParams pReqMsg, |
| struct nlattr **tb) |
| { |
| struct nlattr *bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct nlattr *buckets; |
| struct nlattr *channels; |
| int rem1, rem2; |
| eHalStatus status; |
| uint8_t bktIndex, j, numChannels, total_channels = 0; |
| uint32_t expected_buckets; |
| uint32_t chanList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0}; |
| |
| uint32_t min_dwell_time_active_bucket = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| uint32_t max_dwell_time_active_bucket = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| uint32_t min_dwell_time_passive_bucket = |
| pHddCtx->cfg_ini->extscan_passive_max_chn_time; |
| uint32_t max_dwell_time_passive_bucket = |
| pHddCtx->cfg_ini->extscan_passive_max_chn_time; |
| |
| pReqMsg->min_dwell_time_active = |
| pReqMsg->max_dwell_time_active = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| |
| pReqMsg->min_dwell_time_passive = |
| pReqMsg->max_dwell_time_passive = |
| pHddCtx->cfg_ini->extscan_passive_max_chn_time; |
| |
| expected_buckets = pReqMsg->numBuckets; |
| pReqMsg->numBuckets = 0; |
| bktIndex = 0; |
| |
| nla_for_each_nested(buckets, |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC], rem1) { |
| |
| if (bktIndex >= expected_buckets) { |
| hddLog(LOGW, FL("ignoring excess buckets")); |
| break; |
| } |
| |
| if (nla_parse(bucket, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(buckets), nla_len(buckets), |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch bucket spec */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]) { |
| hddLog(LOGE, FL("attr bucket index failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].bucket = nla_get_u8( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]); |
| |
| /* Parse and fetch wifi band */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]) { |
| hddLog(LOGE, FL("attr wifi band failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].band = nla_get_u8( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]); |
| |
| /* Parse and fetch period */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]) { |
| hddLog(LOGE, FL("attr period failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].period = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]); |
| |
| /* Parse and fetch report events */ |
| if (!bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]) { |
| hddLog(LOGE, FL("attr report events failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].reportEvents = nla_get_u8( |
| bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]); |
| |
| /* Parse and fetch max period */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]) { |
| hddLog(LOGE, FL("attr max period failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].max_period = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]); |
| |
| /* Parse and fetch base */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]) { |
| hddLog(LOGE, FL("attr base failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].exponent = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]); |
| |
| /* Parse and fetch step count */ |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]) { |
| hddLog(LOGE, FL("attr step count failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].step_count = nla_get_u32( |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]); |
| hddLog(LOG1, FL("Bucket spec Index: %d Wifi band: %d period: %d report events: %d max period: %u base: %u Step count: %u"), |
| pReqMsg->buckets[bktIndex].bucket, |
| pReqMsg->buckets[bktIndex].band, |
| pReqMsg->buckets[bktIndex].period, |
| pReqMsg->buckets[bktIndex].reportEvents, |
| pReqMsg->buckets[bktIndex].max_period, |
| pReqMsg->buckets[bktIndex].exponent, |
| pReqMsg->buckets[bktIndex].step_count); |
| |
| /* start with known good values for bucket dwell times */ |
| pReqMsg->buckets[bktIndex].min_dwell_time_active = |
| pReqMsg->buckets[bktIndex].max_dwell_time_active = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| |
| pReqMsg->buckets[bktIndex].min_dwell_time_passive = |
| pReqMsg->buckets[bktIndex].max_dwell_time_passive = |
| pHddCtx->cfg_ini->extscan_passive_max_chn_time; |
| |
| /* Framework shall pass the channel list if the input WiFi band is |
| * WIFI_BAND_UNSPECIFIED. |
| * If the input WiFi band is specified (any value other than |
| * WIFI_BAND_UNSPECIFIED) then driver populates the channel list |
| */ |
| if (pReqMsg->buckets[bktIndex].band != WIFI_BAND_UNSPECIFIED) { |
| if (hdd_extscan_channel_max_reached(pReqMsg, |
| total_channels)) |
| return 0; |
| |
| numChannels = 0; |
| hddLog(LOG1, "WiFi band is specified, driver to fill channel list"); |
| status = sme_GetValidChannelsByBand(pHddCtx->hHal, |
| pReqMsg->buckets[bktIndex].band, |
| chanList, &numChannels); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_GetValidChannelsByBand failed (err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| hddLog(LOG1, FL("before trimming, num_channels: %d"), |
| numChannels); |
| |
| pReqMsg->buckets[bktIndex].numChannels = |
| VOS_MIN(numChannels, |
| (WLAN_EXTSCAN_MAX_CHANNELS - total_channels)); |
| hddLog(LOG1, |
| FL("Adj Num channels/bucket: %d total_channels: %d"), |
| pReqMsg->buckets[bktIndex].numChannels, |
| total_channels); |
| |
| total_channels += pReqMsg->buckets[bktIndex].numChannels; |
| |
| for (j = 0; j < pReqMsg->buckets[bktIndex].numChannels; |
| j++) { |
| pReqMsg->buckets[bktIndex].channels[j].channel = |
| chanList[j]; |
| pReqMsg->buckets[bktIndex].channels[j]. |
| chnlClass = 0; |
| if (CSR_IS_CHANNEL_DFS( |
| vos_freq_to_chan(chanList[j]))) { |
| pReqMsg->buckets[bktIndex].channels[j]. |
| passive = 1; |
| pReqMsg->buckets[bktIndex].channels[j]. |
| dwellTimeMs = |
| pHddCtx->cfg_ini-> |
| extscan_passive_max_chn_time; |
| /* reconfigure per-bucket dwell time */ |
| if (min_dwell_time_passive_bucket > |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| min_dwell_time_passive_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_passive_bucket < |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| max_dwell_time_passive_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| } else { |
| pReqMsg->buckets[bktIndex].channels[j]. |
| passive = 0; |
| pReqMsg->buckets[bktIndex].channels[j]. |
| dwellTimeMs = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| /* reconfigure per-bucket dwell times */ |
| if (min_dwell_time_active_bucket > |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| min_dwell_time_active_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| if (max_dwell_time_active_bucket < |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| max_dwell_time_active_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| } |
| |
| hddLog(LOG1, |
| "Channel %u Passive %u Dwell time %u ms Class %u", |
| pReqMsg->buckets[bktIndex].channels[j].channel, |
| pReqMsg->buckets[bktIndex].channels[j].passive, |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs, |
| pReqMsg->buckets[bktIndex].channels[j].chnlClass); |
| } |
| |
| hdd_extscan_update_dwell_time_limits( |
| pReqMsg, bktIndex, |
| min_dwell_time_active_bucket, |
| max_dwell_time_active_bucket, |
| min_dwell_time_passive_bucket, |
| max_dwell_time_passive_bucket); |
| |
| hddLog(LOG1, FL("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d"), |
| bktIndex, |
| pReqMsg->buckets[bktIndex].min_dwell_time_active, |
| pReqMsg->buckets[bktIndex].max_dwell_time_active, |
| pReqMsg->buckets[bktIndex].min_dwell_time_passive, |
| pReqMsg->buckets[bktIndex].max_dwell_time_passive); |
| |
| bktIndex++; |
| pReqMsg->numBuckets++; |
| continue; |
| } |
| |
| /* Parse and fetch number of channels */ |
| if (!bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]) { |
| hddLog(LOGE, FL("attr num channels failed")); |
| return -EINVAL; |
| } |
| |
| pReqMsg->buckets[bktIndex].numChannels = |
| nla_get_u32(bucket[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]); |
| hddLog(LOG1, FL("before trimming: num channels %d"), |
| pReqMsg->buckets[bktIndex].numChannels); |
| pReqMsg->buckets[bktIndex].numChannels = |
| VOS_MIN(pReqMsg->buckets[bktIndex].numChannels, |
| (WLAN_EXTSCAN_MAX_CHANNELS - total_channels)); |
| hddLog(LOG1, |
| FL("Num channels/bucket: %d total_channels: %d"), |
| pReqMsg->buckets[bktIndex].numChannels, |
| total_channels); |
| if (hdd_extscan_channel_max_reached(pReqMsg, total_channels)) |
| return 0; |
| |
| if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC]) { |
| hddLog(LOGE, FL("attr channel spec failed")); |
| return -EINVAL; |
| } |
| |
| j = 0; |
| nla_for_each_nested(channels, |
| bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC], rem2) { |
| if ((j >= pReqMsg->buckets[bktIndex].numChannels) || |
| hdd_extscan_channel_max_reached(pReqMsg, |
| total_channels)) |
| break; |
| |
| if (nla_parse(channel, |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| nla_data(channels), nla_len(channels), |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch channel */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) { |
| hddLog(LOGE, FL("attr channel failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].channels[j].channel = |
| nla_get_u32(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]); |
| hddLog(LOG1, FL("channel %u"), |
| pReqMsg->buckets[bktIndex].channels[j].channel); |
| |
| /* Parse and fetch dwell time */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) { |
| hddLog(LOGE, FL("attr dwelltime failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs = |
| nla_get_u32(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]); |
| |
| /* Override dwell time if required */ |
| if (pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs < |
| pHddCtx->cfg_ini->extscan_active_min_chn_time || |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs > |
| pHddCtx->cfg_ini->extscan_active_max_chn_time) { |
| hddLog(LOG1, |
| FL("WiFi band is unspecified, dwellTime:%d"), |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs); |
| |
| if (CSR_IS_CHANNEL_DFS( |
| vos_freq_to_chan( |
| pReqMsg->buckets[bktIndex].channels[j].channel))) { |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs = |
| pHddCtx->cfg_ini->extscan_passive_max_chn_time; |
| } else { |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs = |
| pHddCtx->cfg_ini->extscan_active_max_chn_time; |
| } |
| } |
| |
| hddLog(LOG1, FL("New Dwell time (%u ms)"), |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs); |
| |
| if (CSR_IS_CHANNEL_DFS( |
| vos_freq_to_chan( |
| pReqMsg->buckets[bktIndex].channels[j].channel))) { |
| if(min_dwell_time_passive_bucket > |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| min_dwell_time_passive_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| if(max_dwell_time_passive_bucket < |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| max_dwell_time_passive_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| } else { |
| if(min_dwell_time_active_bucket > |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| min_dwell_time_active_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| if(max_dwell_time_active_bucket < |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) { |
| max_dwell_time_active_bucket = |
| pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs; |
| } |
| } |
| |
| /* Parse and fetch channel spec passive */ |
| if (!channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) { |
| hddLog(LOGE, |
| FL("attr channel spec passive failed")); |
| return -EINVAL; |
| } |
| pReqMsg->buckets[bktIndex].channels[j].passive = |
| nla_get_u8(channel[ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]); |
| hddLog(LOG1, FL("Chnl spec passive %u"), |
| pReqMsg->buckets[bktIndex].channels[j].passive); |
| |
| /* Override scan type if required */ |
| if (CSR_IS_CHANNEL_DFS( |
| vos_freq_to_chan( |
| pReqMsg->buckets[bktIndex].channels[j].channel))) { |
| pReqMsg->buckets[bktIndex].channels[j].passive = TRUE; |
| } else { |
| pReqMsg->buckets[bktIndex].channels[j].passive = FALSE; |
| } |
| |
| j++; |
| total_channels++; |
| } |
| |
| hdd_extscan_update_dwell_time_limits( |
| pReqMsg, bktIndex, |
| min_dwell_time_active_bucket, |
| max_dwell_time_active_bucket, |
| min_dwell_time_passive_bucket, |
| max_dwell_time_passive_bucket); |
| |
| hddLog(LOG1, FL("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d"), |
| bktIndex, |
| pReqMsg->buckets[bktIndex].min_dwell_time_active, |
| pReqMsg->buckets[bktIndex].max_dwell_time_active, |
| pReqMsg->buckets[bktIndex].min_dwell_time_passive, |
| pReqMsg->buckets[bktIndex].max_dwell_time_passive); |
| |
| bktIndex++; |
| pReqMsg->numBuckets++; |
| } |
| |
| hddLog(LOG1, FL("Global: actv_min:%d actv_max:%d pass_min:%d pass_max:%d"), |
| pReqMsg->min_dwell_time_active, |
| pReqMsg->max_dwell_time_active, |
| pReqMsg->min_dwell_time_passive, |
| pReqMsg->max_dwell_time_passive); |
| |
| return 0; |
| } |
| |
| /* |
| * hdd_extscan_map_usr_drv_config_flags() - map userspace to driver config flags |
| * @config_flags - [input] configuration flags. |
| * |
| * This function maps user space received configuration flags to |
| * driver representation. |
| * |
| * Return: configuration flags |
| */ |
| static uint32_t hdd_extscan_map_usr_drv_config_flags(uint32_t config_flags) |
| { |
| uint32_t configuration_flags = 0; |
| |
| if (config_flags & EXTSCAN_LP_EXTENDED_BATCHING) |
| configuration_flags |= EXTSCAN_LP_EXTENDED_BATCHING; |
| |
| return configuration_flags; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_start() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| #define PARAM_BASE_PERIOD \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD |
| #define PARAM_MAX_AP_PER_SCAN \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN |
| #define PARAM_RPT_THRHLD_PERCENT \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT |
| #define PARAM_RPT_THRHLD_NUM_SCANS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS |
| #define PARAM_NUM_BUCKETS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS |
| #define PARAM_CONFIG_FLAGS \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS |
| |
| /** |
| * __wlan_hdd_cfg80211_extscan_start() - start extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirWifiScanCmdReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id, num_buckets; |
| eHalStatus status; |
| int retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| |
| if (nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = pAdapter->sessionId; |
| |
| /* Parse and fetch base period */ |
| if (!tb[PARAM_BASE_PERIOD]) { |
| hddLog(LOGE, FL("attr base period failed")); |
| goto fail; |
| } |
| pReqMsg->basePeriod = nla_get_u32(tb[PARAM_BASE_PERIOD]); |
| |
| /* Parse and fetch max AP per scan */ |
| if (!tb[PARAM_MAX_AP_PER_SCAN]) { |
| hddLog(LOGE, FL("attr max_ap_per_scan failed")); |
| goto fail; |
| } |
| pReqMsg->maxAPperScan = nla_get_u32(tb[PARAM_MAX_AP_PER_SCAN]); |
| |
| /* Parse and fetch report threshold percent */ |
| if (!tb[PARAM_RPT_THRHLD_PERCENT]) { |
| hddLog(LOGE, FL("attr report_threshold percent failed")); |
| goto fail; |
| } |
| pReqMsg->report_threshold_percent = nla_get_u8( |
| tb[PARAM_RPT_THRHLD_PERCENT]); |
| |
| /* Parse and fetch report threshold num scans */ |
| if (!tb[PARAM_RPT_THRHLD_NUM_SCANS]) { |
| hddLog(LOGE, FL("attr report_threshold num scans failed")); |
| goto fail; |
| } |
| pReqMsg->report_threshold_num_scans = nla_get_u8( |
| tb[PARAM_RPT_THRHLD_NUM_SCANS]); |
| hddLog(LOG1, FL("Req Id: %d Session Id: %d Base Period: %d Max AP per Scan: %d Rpt Thld percent: %d Rpt Thld num scans: %d"), |
| pReqMsg->requestId, pReqMsg->sessionId, |
| pReqMsg->basePeriod, pReqMsg->maxAPperScan, |
| pReqMsg->report_threshold_percent, |
| pReqMsg->report_threshold_num_scans); |
| |
| /* Parse and fetch number of buckets */ |
| if (!tb[PARAM_NUM_BUCKETS]) { |
| hddLog(LOGE, FL("attr number of buckets failed")); |
| goto fail; |
| } |
| num_buckets = nla_get_u8(tb[PARAM_NUM_BUCKETS]); |
| if (num_buckets > WLAN_EXTSCAN_MAX_BUCKETS) { |
| hddLog(LOGW, |
| FL("Exceeded MAX number of buckets: %d"), |
| WLAN_EXTSCAN_MAX_BUCKETS); |
| num_buckets = WLAN_EXTSCAN_MAX_BUCKETS; |
| } |
| hddLog(LOG1, FL("Input: Number of Buckets %d"), num_buckets); |
| pReqMsg->numBuckets = num_buckets; |
| |
| /* This is optional attribute, if not present set it to 0 */ |
| if (!tb[PARAM_CONFIG_FLAGS]) |
| pReqMsg->configuration_flags = 0; |
| else |
| pReqMsg->configuration_flags = |
| hdd_extscan_map_usr_drv_config_flags( |
| nla_get_u32(tb[PARAM_CONFIG_FLAGS])); |
| |
| hddLog(LOG1, FL("Configuration flags: %u"), |
| pReqMsg->configuration_flags); |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC]) { |
| hddLog(LOGE, FL("attr bucket spec failed")); |
| goto fail; |
| } |
| |
| if (hdd_extscan_start_fill_bucket_channel_spec(pHddCtx, pReqMsg, tb)) |
| goto fail; |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| context->buckets_scanned = 0; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_ExtScanStart(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_ExtScanStart failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| pHddCtx->ext_scan_start_since_boot = vos_get_monotonic_boottime(); |
| hddLog(LOG1, FL("Timestamp since boot: %llu"), |
| pHddCtx->ext_scan_start_since_boot); |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("sme_ExtScanStart timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_start() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_BASE_PERIOD |
| #undef PARAMS_MAX_AP_PER_SCAN |
| #undef PARAMS_RPT_THRHLD_PERCENT |
| #undef PARAMS_RPT_THRHLD_NUM_SCANS |
| #undef PARAMS_NUM_BUCKETS |
| #undef PARAM_CONFIG_FLAGS |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_start() - start extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_stop() |
| */ |
| #define PARAM_MAX \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX |
| #define PARAM_REQUEST_ID \ |
| QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_stop() - stop extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int __wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanStopReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct hdd_ext_scan_context *context; |
| struct nlattr *tb[PARAM_MAX + 1]; |
| uint32_t request_id; |
| eHalStatus status; |
| int retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, PARAM_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(LOG1, FL("Req Id %d Session Id %d"), |
| pReqMsg->requestId, pReqMsg->sessionId); |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_ExtScanStop(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_ExtScanStop failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("sme_ExtScanStop timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_stop() - stop extscan |
| * @wiphy: Pointer to wireless phy. |
| * @wdev: Pointer to wireless device. |
| * @data: Pointer to input data. |
| * @data_len: Length of @data. |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * done with short names for the global vendor params |
| * used by wlan_hdd_cfg80211_extscan_stop() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_REQUEST_ID |
| |
| static int __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanResetBssidHotlistReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id; |
| eHalStatus status; |
| int retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(LOG1, FL("Req Id %d Session Id %d"), |
| pReqMsg->requestId, pReqMsg->sessionId); |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_ResetBssHotlist(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_ResetBssHotlist failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| if (!rc) { |
| hddLog(LOGE, FL("sme_ResetBssHotlist timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hot list |
| * @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_reset_bssid_hotlist(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static int __wlan_hdd_cfg80211_extscan_reset_significant_change( |
| struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| tpSirExtScanResetSignificantChangeReqParams pReqMsg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1]; |
| struct hdd_ext_scan_context *context; |
| uint32_t request_id; |
| eHalStatus status; |
| int retval; |
| unsigned long rc; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| retval = wlan_hdd_validate_context(pHddCtx); |
| if (0 != retval) |
| return -EINVAL; |
| |
| if (!pHddCtx->cfg_ini->extscan_enabled) { |
| hddLog(LOGE, FL("extscan not supported")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX, |
| data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| pReqMsg = vos_mem_malloc(sizeof(*pReqMsg)); |
| if (!pReqMsg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| pReqMsg->requestId = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| pReqMsg->sessionId = pAdapter->sessionId; |
| hddLog(LOG1, FL("Req Id %d Session Id %d"), |
| pReqMsg->requestId, pReqMsg->sessionId); |
| |
| context = &pHddCtx->ext_scan_context; |
| spin_lock(&hdd_context_lock); |
| INIT_COMPLETION(context->response_event); |
| context->request_id = request_id = pReqMsg->requestId; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_ResetSignificantChange(pHddCtx->hHal, pReqMsg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_ResetSignificantChange failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN)); |
| |
| if (!rc) { |
| hddLog(LOGE, FL("sme_ResetSignificantChange timed out")); |
| retval = -ETIMEDOUT; |
| } else { |
| spin_lock(&hdd_context_lock); |
| if (context->request_id == request_id) |
| retval = context->response_status; |
| else |
| retval = -EINVAL; |
| spin_unlock(&hdd_context_lock); |
| } |
| EXIT(); |
| return retval; |
| |
| fail: |
| vos_mem_free(pReqMsg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_extscan_reset_significant_change() - reset significant |
| * change |
| * @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_reset_significant_change(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_extscan_reset_significant_change(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| |
| /** |
| * hdd_extscan_epno_fill_network_list() - epno fill network list |
| * @hddctx: HDD context |
| * @req_msg: request message |
| * @tb: vendor attribute table |
| * |
| * This function reads the network block NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_extscan_epno_fill_network_list( |
| hdd_context_t *hddctx, |
| struct wifi_epno_params *req_msg, |
| struct nlattr **tb) |
| { |
| struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| struct nlattr *networks; |
| int rem1, ssid_len; |
| uint8_t index, *ssid; |
| uint32_t expected_networks; |
| |
| expected_networks = req_msg->num_networks; |
| index = 0; |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST]) { |
| hddLog(LOGE, FL("attr networks list failed")); |
| return -EINVAL; |
| } |
| nla_for_each_nested(networks, |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST], |
| rem1) { |
| |
| if (index == expected_networks) { |
| hddLog(LOGW, FL("ignoring excess networks")); |
| break; |
| } |
| |
| if (nla_parse(network, QCA_WLAN_VENDOR_ATTR_PNO_MAX, |
| nla_data(networks), nla_len(networks), |
| wlan_hdd_pno_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ssid */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]) { |
| hddLog(LOGE, FL("attr network ssid failed")); |
| return -EINVAL; |
| } |
| ssid_len = nla_len( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]); |
| |
| /* nla_parse will detect overflow but not underflow */ |
| if (0 == ssid_len) { |
| hddLog(LOGE, FL("zero ssid length")); |
| return -EINVAL; |
| } |
| |
| /* Decrement by 1, don't count null character */ |
| ssid_len--; |
| |
| req_msg->networks[index].ssid.length = ssid_len; |
| hddLog(LOG1, FL("network ssid length %d"), ssid_len); |
| ssid = nla_data(network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]); |
| vos_mem_copy(req_msg->networks[index].ssid.ssId, ssid, ssid_len); |
| hddLog(LOG1, FL("Ssid: %.*s"), |
| req_msg->networks[index].ssid.length, |
| req_msg->networks[index].ssid.ssId); |
| |
| /* Parse and fetch epno flags */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]) { |
| hddLog(LOGE, FL("attr epno flags failed")); |
| return -EINVAL; |
| } |
| req_msg->networks[index].flags = nla_get_u8( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]); |
| hddLog(LOG1, FL("flags %u"), req_msg->networks[index].flags); |
| |
| /* Parse and fetch auth bit */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]) { |
| hddLog(LOGE, FL("attr auth bit failed")); |
| return -EINVAL; |
| } |
| req_msg->networks[index].auth_bit_field = nla_get_u8( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]); |
| hddLog(LOG1, FL("auth bit %u"), |
| req_msg->networks[index].auth_bit_field); |
| |
| index++; |
| } |
| req_msg->num_networks = index; |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_epno_list() - epno set network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_epno_params *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[ |
| QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| eHalStatus status; |
| uint32_t num_networks, len; |
| int ret_val; |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, |
| data, data_len, |
| wlan_hdd_pno_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch number of networks */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]) { |
| hddLog(LOGE, FL("attr num networks failed")); |
| return -EINVAL; |
| } |
| /* |
| * num_networks is also used as EPNO SET/RESET request. |
| * if num_networks is zero then it is treated as RESET. |
| */ |
| num_networks = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]); |
| |
| if (num_networks > MAX_EPNO_NETWORKS) { |
| hddLog(LOG1, |
| FL("input num_networks: %d exceeded max: %d, hence reset to: %d"), |
| num_networks, MAX_EPNO_NETWORKS, MAX_EPNO_NETWORKS); |
| num_networks = MAX_EPNO_NETWORKS; |
| } |
| |
| hddLog(LOG1, FL("num networks %u"), num_networks); |
| len = sizeof(*req_msg) + |
| (num_networks * sizeof(struct wifi_epno_network)); |
| |
| req_msg = vos_mem_malloc(len); |
| if (!req_msg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| vos_mem_zero(req_msg, len); |
| req_msg->num_networks = num_networks; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]); |
| |
| req_msg->session_id = adapter->sessionId; |
| hddLog(LOG1, FL("Req Id %u Session Id %d"), |
| req_msg->request_id, req_msg->session_id); |
| |
| if (num_networks) { |
| |
| /* Parse and fetch min_5ghz_rssi */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]) { |
| hddLog(LOGE, FL("min_5ghz_rssi id failed")); |
| goto fail; |
| } |
| req_msg->min_5ghz_rssi = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]); |
| |
| /* Parse and fetch min_24ghz_rssi */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]) { |
| hddLog(LOGE, FL("min_24ghz_rssi id failed")); |
| goto fail; |
| } |
| req_msg->min_24ghz_rssi = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]); |
| |
| /* Parse and fetch initial_score_max */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]) { |
| hddLog(LOGE, FL("initial_score_max id failed")); |
| goto fail; |
| } |
| req_msg->initial_score_max = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]); |
| |
| /* Parse and fetch current_connection_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS]) { |
| hddLog(LOGE, FL("current_connection_bonus id failed")); |
| goto fail; |
| } |
| req_msg->current_connection_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS] |
| ); |
| |
| /* Parse and fetch same_network_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]) { |
| hddLog(LOGE, FL("same_network_bonus id failed")); |
| goto fail; |
| } |
| req_msg->same_network_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]); |
| |
| /* Parse and fetch secure_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]) { |
| hddLog(LOGE, FL("secure_bonus id failed")); |
| goto fail; |
| } |
| req_msg->secure_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]); |
| |
| /* Parse and fetch band_5ghz_bonus */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]) { |
| hddLog(LOGE, FL("band_5ghz_bonus id failed")); |
| goto fail; |
| } |
| req_msg->band_5ghz_bonus = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]); |
| |
| hddLog(LOG1, |
| FL("min_5ghz_rssi: %d min_24ghz_rssi: %d initial_score_max: %d current_connection_bonus: %d"), |
| req_msg->min_5ghz_rssi, |
| req_msg->min_24ghz_rssi, |
| req_msg->initial_score_max, |
| req_msg->current_connection_bonus); |
| hddLog(LOG1, |
| FL("same_network_bonus: %d secure_bonus: %d band_5ghz_bonus: %d"), |
| req_msg->same_network_bonus, |
| req_msg->secure_bonus, |
| req_msg->band_5ghz_bonus); |
| |
| if (hdd_extscan_epno_fill_network_list(hdd_ctx, req_msg, tb)) |
| goto fail; |
| |
| } |
| |
| status = sme_set_epno_list(hdd_ctx->hHal, req_msg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_set_epno_list failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| EXIT(); |
| vos_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| vos_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_epno_list() - epno set network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the epno request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_epno_list(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_extscan_passpoint_fill_network_list() - passpoint fill network list |
| * @hddctx: HDD context |
| * @req_msg: request message |
| * @tb: vendor attribute table |
| * |
| * This function reads the network block NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int hdd_extscan_passpoint_fill_network_list( |
| hdd_context_t *hddctx, |
| struct wifi_passpoint_req *req_msg, |
| struct nlattr **tb) |
| { |
| struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| struct nlattr *networks; |
| int rem1, len; |
| uint8_t index; |
| uint32_t expected_networks; |
| |
| expected_networks = req_msg->num_networks; |
| index = 0; |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY]) { |
| hddLog(LOGE, FL("attr network array failed")); |
| return -EINVAL; |
| } |
| nla_for_each_nested(networks, |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY], |
| rem1) { |
| |
| if (index == expected_networks) { |
| hddLog(LOGW, FL("ignoring excess networks")); |
| break; |
| } |
| |
| if (nla_parse(network, |
| QCA_WLAN_VENDOR_ATTR_PNO_MAX, |
| nla_data(networks), nla_len(networks), |
| wlan_hdd_pno_config_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch identifier */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID]) { |
| hddLog(LOGE, FL("attr passpoint id failed")); |
| return -EINVAL; |
| } |
| req_msg->networks[index].id = nla_get_u32( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID]); |
| hddLog(LOG1, FL("Id %u"), req_msg->networks[index].id); |
| |
| /* Parse and fetch realm */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]) { |
| hddLog(LOGE, FL("attr realm failed")); |
| return -EINVAL; |
| } |
| len = nla_len( |
| network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]); |
| if (len < 0 || len > SIR_PASSPOINT_REALM_LEN) { |
| hddLog(LOGE, FL("Invalid realm size %d"), len); |
| return -EINVAL; |
| } |
| vos_mem_copy(req_msg->networks[index].realm, |
| nla_data(network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]), |
| len); |
| hddLog(LOG1, FL("realm len %d"), len); |
| hddLog(LOG1, FL("realm: %s"), req_msg->networks[index].realm); |
| |
| /* Parse and fetch roaming consortium ids */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID]) { |
| hddLog(LOGE, FL("attr roaming consortium ids failed")); |
| return -EINVAL; |
| } |
| nla_memcpy(&req_msg->networks[index].roaming_consortium_ids, |
| network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID], |
| sizeof(req_msg->networks[0].roaming_consortium_ids)); |
| hddLog(LOG1, FL("roaming consortium ids")); |
| VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| req_msg->networks[index].roaming_consortium_ids, |
| sizeof(req_msg->networks[0].roaming_consortium_ids)); |
| |
| /* Parse and fetch plmn */ |
| if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN]) { |
| hddLog(LOGE, FL("attr plmn failed")); |
| return -EINVAL; |
| } |
| nla_memcpy(&req_msg->networks[index].plmn, |
| network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN], |
| SIR_PASSPOINT_PLMN_LEN); |
| hddLog(LOG1, FL("plmn %02x:%02x:%02x"), |
| req_msg->networks[index].plmn[0], |
| req_msg->networks[index].plmn[1], |
| req_msg->networks[index].plmn[2]); |
| |
| index++; |
| } |
| req_msg->num_networks = index; |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_passpoint_req *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| eHalStatus status; |
| uint32_t num_networks = 0; |
| int ret; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, data_len, |
| wlan_hdd_pno_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch number of networks */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]) { |
| hddLog(LOGE, FL("attr num networks failed")); |
| return -EINVAL; |
| } |
| num_networks = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]); |
| if (num_networks > SIR_PASSPOINT_LIST_MAX_NETWORKS) { |
| hddLog(LOGE, FL("num networks %u exceeds max %u"), |
| num_networks, SIR_PASSPOINT_LIST_MAX_NETWORKS); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG1, FL("num networks %u"), num_networks); |
| req_msg = vos_mem_malloc(sizeof(*req_msg) + |
| (num_networks * sizeof(req_msg->networks[0]))); |
| if (!req_msg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| req_msg->num_networks = num_networks; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| req_msg->session_id = adapter->sessionId; |
| hddLog(LOG1, FL("Req Id %u Session Id %d"), req_msg->request_id, |
| req_msg->session_id); |
| |
| if (hdd_extscan_passpoint_fill_network_list(hdd_ctx, req_msg, tb)) |
| goto fail; |
| |
| status = sme_set_passpoint_list(hdd_ctx->hHal, req_msg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_set_passpoint_list failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| EXIT(); |
| vos_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| vos_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function reads the NL vendor attributes from %tb and |
| * fill in the passpoint request message. |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_passpoint_list(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function resets passpoint networks list |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int __wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct wifi_passpoint_req *req_msg = NULL; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1]; |
| eHalStatus status; |
| int ret; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, data_len, |
| wlan_hdd_extscan_config_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| req_msg = vos_mem_malloc(sizeof(*req_msg)); |
| if (!req_msg) { |
| hddLog(LOGE, FL("vos_mem_malloc failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| req_msg->request_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]); |
| |
| req_msg->session_id = adapter->sessionId; |
| hddLog(LOG1, FL("Req Id %u Session Id %d"), |
| req_msg->request_id, req_msg->session_id); |
| |
| status = sme_reset_passpoint_list(hdd_ctx->hHal, req_msg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_reset_passpoint_list failed(err=%d)"), status); |
| goto fail; |
| } |
| |
| EXIT(); |
| vos_mem_free(req_msg); |
| return 0; |
| |
| fail: |
| vos_mem_free(req_msg); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list |
| * @wiphy: wiphy |
| * @wdev: pointer to wireless dev |
| * @data: data pointer |
| * @data_len: data length |
| * |
| * This function resets passpoint networks list |
| * |
| * Return: 0 on success, error number otherwise |
| */ |
| static int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_reset_passpoint_list(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| /** |
| * 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 |
| 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. |
| */ |
| 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; |
| uint8_t feature_flags[(NUM_QCA_WLAN_VENDOR_FEATURES + 7) / 8] = {0}; |
| int ret_val; |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| |
| ret_val = wlan_hdd_validate_context(pHddCtx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| #ifdef WLAN_FEATURE_ROAM_OFFLOAD |
| if (pHddCtx->cfg_ini->isRoamOffloadEnabled) { |
| hddLog(LOG1, FL("Key Mgmt Offload is supported")); |
| wlan_hdd_cfg80211_set_feature (feature_flags, |
| QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD); |
| } |
| #endif |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("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; |
| |
| 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_features(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #ifdef WLAN_FEATURE_LINK_LAYER_STATS |
| |
| static bool put_wifi_rate_stat( tpSirWifiRateStat stats, |
| struct sk_buff *vendor_event) |
| { |
| if (nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE, |
| stats->rate.preamble) || |
| nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS, |
| stats->rate.nss) || |
| nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW, |
| stats->rate.bw) || |
| nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX, |
| stats->rate.rateMcsIdx) || |
| nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE, |
| stats->rate.bitrate ) || |
| nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU, |
| stats->txMpdu ) || |
| nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU, |
| stats->rxMpdu ) || |
| nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST, |
| stats->mpduLost ) || |
| nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES, |
| stats->retries) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT, |
| stats->retriesShort ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG, |
| stats->retriesLong)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static bool put_wifi_peer_info( tpSirWifiPeerInfo stats, |
| struct sk_buff *vendor_event) |
| { |
| u32 i = 0; |
| tpSirWifiRateStat pRateStats; |
| |
| if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE, |
| stats->type) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS, |
| VOS_MAC_ADDR_SIZE, &stats->peerMacAddress[0]) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES, |
| stats->capabilities) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES, |
| stats->numRate)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| goto error; |
| } |
| |
| if (stats->numRate) |
| { |
| struct nlattr *rateInfo; |
| struct nlattr *rates; |
| |
| rateInfo = nla_nest_start(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO); |
| if (rateInfo == NULL) |
| goto error; |
| |
| for (i = 0; i < stats->numRate; i++) |
| { |
| pRateStats = (tpSirWifiRateStat )((uint8 *) |
| stats->rateStats + |
| (i * sizeof(tSirWifiRateStat))); |
| rates = nla_nest_start(vendor_event, i); |
| if (rates == NULL) |
| goto error; |
| |
| if (FALSE == put_wifi_rate_stat(pRateStats, vendor_event)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return FALSE; |
| } |
| nla_nest_end(vendor_event, rates); |
| } |
| nla_nest_end(vendor_event, rateInfo); |
| } |
| |
| return TRUE; |
| error: |
| return FALSE; |
| } |
| |
| static bool put_wifi_wmm_ac_stat( tpSirWifiWmmAcStat stats, |
| struct sk_buff *vendor_event) |
| { |
| if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC, |
| stats->ac ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU, |
| stats->pending_msdu ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU, |
| stats->txMpdu ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU, |
| stats->rxMpdu ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST, |
| stats->txMcast ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST, |
| stats->rxMcast ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU, |
| stats->rxAmpdu ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU, |
| stats->txAmpdu ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST, |
| stats->mpduLost )|| |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES, |
| stats->retries ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT, |
| stats->retriesShort ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG, |
| stats->retriesLong ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN, |
| stats->contentionTimeMin ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX, |
| stats->contentionTimeMax ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG, |
| stats->contentionTimeAvg ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES, |
| stats->contentionNumSamples )) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail") ); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static bool put_wifi_interface_info(tpSirWifiInterfaceInfo stats, |
| struct sk_buff *vendor_event) |
| { |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE, |
| stats->mode ) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR, |
| VOS_MAC_ADDR_SIZE, stats->macAddr) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE, |
| stats->state ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING, |
| stats->roaming ) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES, |
| stats->capabilities ) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID, |
| strlen(stats->ssid), stats->ssid) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID, |
| VOS_MAC_ADDR_SIZE, stats->bssid) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR, |
| WNI_CFG_COUNTRY_CODE_LEN, stats->apCountryStr) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR, |
| WNI_CFG_COUNTRY_CODE_LEN, stats->countryStr)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail") ); |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static bool put_wifi_iface_stats(tpSirWifiIfaceStat pWifiIfaceStat, |
| u32 num_peers, |
| struct sk_buff *vendor_event) |
| { |
| int i = 0; |
| struct nlattr *wmmInfo; |
| struct nlattr *wmmStats; |
| u64 average_tsf_offset; |
| |
| if (FALSE == put_wifi_interface_info( |
| &pWifiIfaceStat->info, |
| vendor_event)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail") ); |
| return FALSE; |
| |
| } |
| |
| average_tsf_offset = pWifiIfaceStat->avg_bcn_spread_offset_high; |
| average_tsf_offset = (average_tsf_offset << 32) | |
| pWifiIfaceStat->avg_bcn_spread_offset_low ; |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_IFACE) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS, |
| num_peers) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX, |
| pWifiIfaceStat->beaconRx) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX, |
| pWifiIfaceStat->mgmtRx) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX, |
| pWifiIfaceStat->mgmtActionRx) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX, |
| pWifiIfaceStat->mgmtActionTx) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT, |
| pWifiIfaceStat->rssiMgmt) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA, |
| pWifiIfaceStat->rssiData) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK, |
| pWifiIfaceStat->rssiAck) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED, |
| pWifiIfaceStat->is_leaky_ap) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED, |
| pWifiIfaceStat->avg_rx_frms_leaked) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME, |
| pWifiIfaceStat->rx_leak_window) || |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
| nla_put_u64_64bit(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET, |
| average_tsf_offset, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PAD) || |
| #else |
| nla_put_u64(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET, |
| average_tsf_offset) || |
| #endif |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT, |
| pWifiIfaceStat->rts_succ_cnt) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT, |
| pWifiIfaceStat->rts_fail_cnt) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT, |
| pWifiIfaceStat->ppdu_succ_cnt) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT, |
| pWifiIfaceStat->ppdu_fail_cnt)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return FALSE; |
| } |
| |
| wmmInfo = nla_nest_start(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO); |
| if (wmmInfo == NULL) |
| return FALSE; |
| |
| for (i = 0; i < WIFI_AC_MAX; i++) |
| { |
| wmmStats = nla_nest_start(vendor_event, i); |
| if (wmmStats == NULL) |
| return FALSE; |
| |
| if (FALSE == put_wifi_wmm_ac_stat( |
| &pWifiIfaceStat->AccessclassStats[i], |
| vendor_event)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("put_wifi_wmm_ac_stat Fail")); |
| return FALSE; |
| } |
| |
| nla_nest_end(vendor_event, wmmStats); |
| } |
| nla_nest_end(vendor_event, wmmInfo); |
| return TRUE; |
| } |
| |
| static tSirWifiInterfaceMode |
| hdd_map_device_to_ll_iface_mode ( int deviceMode ) |
| { |
| switch (deviceMode) |
| { |
| case WLAN_HDD_INFRA_STATION: |
| return WIFI_INTERFACE_STA; |
| case WLAN_HDD_SOFTAP: |
| return WIFI_INTERFACE_SOFTAP; |
| case WLAN_HDD_P2P_CLIENT: |
| return WIFI_INTERFACE_P2P_CLIENT; |
| case WLAN_HDD_P2P_GO: |
| return WIFI_INTERFACE_P2P_GO; |
| case WLAN_HDD_IBSS: |
| return WIFI_INTERFACE_IBSS; |
| default: |
| /* Return Interface Mode as STA for all the unsupported modes */ |
| return WIFI_INTERFACE_STA; |
| } |
| } |
| |
| static bool hdd_get_interface_info(hdd_adapter_t *pAdapter, |
| tpSirWifiInterfaceInfo pInfo) |
| { |
| v_U8_t *staMac = NULL; |
| hdd_station_ctx_t *pHddStaCtx; |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| tpAniSirGlobal pMac = PMAC_STRUCT( hHal ); |
| |
| pInfo->mode = hdd_map_device_to_ll_iface_mode(pAdapter->device_mode); |
| |
| vos_mem_copy(pInfo->macAddr, |
| pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t)); |
| |
| if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) || |
| (WLAN_HDD_P2P_DEVICE == pAdapter->device_mode))) |
| { |
| pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| if (eConnectionState_NotConnected == pHddStaCtx->conn_info.connState) |
| { |
| pInfo->state = WIFI_DISCONNECTED; |
| } |
| if (eConnectionState_Connecting == pHddStaCtx->conn_info.connState) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Session ID %d, Connection is in progress", __func__, |
| pAdapter->sessionId); |
| pInfo->state = WIFI_ASSOCIATING; |
| } |
| if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) && |
| (VOS_FALSE == pHddStaCtx->conn_info.uIsAuthenticated)) |
| { |
| staMac = (v_U8_t *) &(pAdapter->macAddressCurrent.bytes[0]); |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: client " MAC_ADDRESS_STR |
| " is in the middle of WPS/EAPOL exchange.", __func__, |
| MAC_ADDR_ARRAY(staMac)); |
| pInfo->state = WIFI_AUTHENTICATING; |
| } |
| if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) |
| { |
| pInfo->state = WIFI_ASSOCIATED; |
| vos_mem_copy(pInfo->bssid, |
| &pHddStaCtx->conn_info.bssId, VOS_MAC_ADDR_SIZE); |
| vos_mem_copy(pInfo->ssid, |
| pHddStaCtx->conn_info.SSID.SSID.ssId, |
| pHddStaCtx->conn_info.SSID.SSID.length); |
| /* |
| * NULL Terminate the string |
| */ |
| pInfo->ssid[pHddStaCtx->conn_info.SSID.SSID.length] = 0; |
| } |
| } |
| |
| vos_mem_copy(pInfo->countryStr, |
| pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN); |
| |
| vos_mem_copy(pInfo->apCountryStr, |
| pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN); |
| |
| return TRUE; |
| } |
| |
| /* |
| * hdd_link_layer_process_peer_stats () - This function is called after |
| * receiving Link Layer Peer statistics from FW.This function converts |
| * the firmware data to the NL data and sends the same to the kernel/upper |
| * layers. |
| */ |
| static void hdd_link_layer_process_peer_stats(hdd_adapter_t *pAdapter, |
| u32 more_data, |
| tpSirWifiPeerStat pData) |
| { |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| tpSirWifiPeerStat pWifiPeerStat; |
| tpSirWifiPeerInfo pWifiPeerInfo; |
| struct sk_buff *vendor_event; |
| int status, i; |
| struct nlattr *peers; |
| int numRate; |
| |
| ENTER(); |
| |
| pWifiPeerStat = pData; |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "LL_STATS_PEER_ALL : numPeers %u, more data = %u", |
| pWifiPeerStat->numPeers, |
| more_data); |
| |
| /* |
| * Allocate a size of 4096 for the peer stats comprising |
| * each of size = sizeof (tSirWifiPeerInfo) + numRate * |
| * sizeof (tSirWifiRateStat).Each field is put with an |
| * NL attribute.The size of 4096 is considered assuming |
| * that number of rates shall not exceed beyond 50 with |
| * the sizeof (tSirWifiRateStat) being 32. |
| */ |
| vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy, |
| LL_STATS_EVENT_BUF_SIZE); |
| |
| if (!vendor_event) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: cfg80211_vendor_cmd_alloc_reply_skb failed", |
| __func__); |
| return; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_PEER) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA, |
| more_data) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS, |
| pWifiPeerStat->numPeers)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: QCA_WLAN_VENDOR_ATTR put fail", __func__); |
| |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| |
| pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8 *) |
| pWifiPeerStat->peerInfo); |
| |
| if (pWifiPeerStat->numPeers) |
| { |
| struct nlattr *peerInfo; |
| peerInfo = nla_nest_start(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO); |
| if (peerInfo == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| for (i = 1; i <= pWifiPeerStat->numPeers; i++) |
| { |
| peers = nla_nest_start(vendor_event, i); |
| if (peers == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| numRate = pWifiPeerInfo->numRate; |
| |
| if (FALSE == put_wifi_peer_info( |
| pWifiPeerInfo, vendor_event)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("put_wifi_peer_info fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8 *) |
| pWifiPeerStat->peerInfo + |
| (i * sizeof(tSirWifiPeerInfo)) + |
| (numRate * sizeof (tSirWifiRateStat))); |
| nla_nest_end(vendor_event, peers); |
| } |
| nla_nest_end(vendor_event, peerInfo); |
| } |
| cfg80211_vendor_cmd_reply(vendor_event); |
| EXIT(); |
| } |
| |
| /* |
| * hdd_link_layer_process_iface_stats () - This function is called after |
| * receiving Link Layer Interface statistics from FW.This function converts |
| * the firmware data to the NL data and sends the same to the kernel/upper |
| * layers. |
| */ |
| static void hdd_link_layer_process_iface_stats(hdd_adapter_t *pAdapter, |
| tpSirWifiIfaceStat pData, |
| u32 num_peers) |
| { |
| tpSirWifiIfaceStat pWifiIfaceStat; |
| struct sk_buff *vendor_event; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| int status; |
| |
| ENTER(); |
| |
| pWifiIfaceStat = pData; |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return; |
| |
| /* |
| * Allocate a size of 4096 for the interface stats comprising |
| * sizeof (tpSirWifiIfaceStat).The size of 4096 is considered |
| * assuming that all these fit with in the limit.Please take |
| * a call on the limit based on the data requirements on |
| * interface statistics. |
| */ |
| vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy, |
| LL_STATS_EVENT_BUF_SIZE); |
| |
| if (!vendor_event) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_cmd_alloc_reply_skb failed") ); |
| return; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, "WMI_LINK_STATS_IFACE Data"); |
| |
| if (FALSE == hdd_get_interface_info(pAdapter, |
| &pWifiIfaceStat->info)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("hdd_get_interface_info get fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (FALSE == put_wifi_iface_stats(pWifiIfaceStat, num_peers, vendor_event)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("put_wifi_iface_stats fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| cfg80211_vendor_cmd_reply(vendor_event); |
| EXIT(); |
| } |
| |
| /* |
| * hdd_link_layer_process_radio_stats () - This function is called after |
| * receiving Link Layer Radio statistics from FW.This function converts |
| * the firmware data to the NL data and sends the same to the kernel/upper |
| * layers. |
| */ |
| static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter, |
| u32 more_data, |
| tpSirWifiRadioStat pData, |
| u32 num_radio) |
| { |
| int status, i; |
| tpSirWifiRadioStat pWifiRadioStat; |
| tpSirWifiChannelStats pWifiChannelStats; |
| struct sk_buff *vendor_event; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| |
| ENTER(); |
| |
| pWifiRadioStat = pData; |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return; |
| |
| hddLog(LOG1, |
| "LL_STATS_RADIO" |
| " number of radios: %u radio: %d onTime: %u" |
| " txTime: %u rxTime: %u onTimeScan: %u onTimeNbd: %u" |
| " onTimeGscan: %u onTimeRoamScan: %u" |
| " onTimePnoScan: %u onTimeHs20: %u" |
| " numChannels: %u total_num_tx_power_levels: %u", |
| num_radio, pWifiRadioStat->radio, pWifiRadioStat->onTime, |
| pWifiRadioStat->txTime, pWifiRadioStat->rxTime, |
| pWifiRadioStat->onTimeScan, pWifiRadioStat->onTimeNbd, |
| pWifiRadioStat->onTimeGscan, pWifiRadioStat->onTimeRoamScan, |
| pWifiRadioStat->onTimePnoScan, pWifiRadioStat->onTimeHs20, |
| pWifiRadioStat->numChannels, |
| pWifiRadioStat->total_num_tx_power_levels); |
| |
| /* |
| * Allocate a size of 4096 for the Radio stats comprising |
| * sizeof (tSirWifiRadioStat) + numChannels * sizeof |
| * (tSirWifiChannelStats).Each channel data is put with an |
| * NL attribute.The size of 4096 is considered assuming that |
| * number of channels shall not exceed beyond 60 with the |
| * sizeof (tSirWifiChannelStats) being 24 bytes. |
| */ |
| |
| vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy, |
| LL_STATS_EVENT_BUF_SIZE); |
| |
| if (!vendor_event) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA, |
| more_data) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS, |
| num_radio) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID, |
| pWifiRadioStat->radio) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME, |
| pWifiRadioStat->onTime) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME, |
| pWifiRadioStat->txTime) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME, |
| pWifiRadioStat->rxTime) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN, |
| pWifiRadioStat->onTimeScan) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD, |
| pWifiRadioStat->onTimeNbd) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN, |
| pWifiRadioStat->onTimeGscan)|| |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN, |
| pWifiRadioStat->onTimeRoamScan) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN, |
| pWifiRadioStat->onTimePnoScan) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20, |
| pWifiRadioStat->onTimeHs20) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS, |
| pWifiRadioStat->total_num_tx_power_levels) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS, |
| pWifiRadioStat->numChannels)) { |
| hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| |
| kfree_skb(vendor_event); |
| return ; |
| } |
| |
| if (pWifiRadioStat->total_num_tx_power_levels) { |
| if (nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL, |
| sizeof(u32) * pWifiRadioStat->total_num_tx_power_levels, |
| pWifiRadioStat->tx_time_per_power_level)) { |
| hddLog(LOGE, FL("ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| } |
| |
| if (pWifiRadioStat->numChannels) |
| { |
| struct nlattr *chList; |
| struct nlattr *chInfo; |
| |
| chList = nla_nest_start(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO); |
| if (chList == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| for (i = 0; i < pWifiRadioStat->numChannels; i++) |
| { |
| pWifiChannelStats = (tpSirWifiChannelStats) ((uint8*) |
| pWifiRadioStat->channels + |
| (i * sizeof(tSirWifiChannelStats))); |
| |
| chInfo = nla_nest_start(vendor_event, i); |
| if (chInfo == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH, |
| pWifiChannelStats->channel.width) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ, |
| pWifiChannelStats->channel.centerFreq) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0, |
| pWifiChannelStats->channel.centerFreq0) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1, |
| pWifiChannelStats->channel.centerFreq1) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME, |
| pWifiChannelStats->onTime) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME, |
| pWifiChannelStats->ccaBusyTime)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_put failed")); |
| kfree_skb(vendor_event); |
| return ; |
| } |
| nla_nest_end(vendor_event, chInfo); |
| } |
| nla_nest_end(vendor_event, chList); |
| } |
| cfg80211_vendor_cmd_reply(vendor_event); |
| EXIT(); |
| } |
| |
| /* |
| * wlan_hdd_cfg80211_link_layer_stats_callback () - This function is called |
| * after receiving Link Layer indications from FW.This callback converts the |
| * firmware data to the NL data and send the same to the kernel/upper layers. |
| */ |
| static void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx, int indType, |
| void *pRsp) |
| { |
| hdd_adapter_t *pAdapter = NULL; |
| struct hdd_ll_stats_context *context; |
| hdd_context_t *pHddCtx = ctx; |
| tpSirLLStatsResults linkLayerStatsResults = (tpSirLLStatsResults)pRsp; |
| int status; |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| |
| if (0 != status) |
| return; |
| |
| pAdapter = hdd_get_adapter_by_vdev(pHddCtx, |
| linkLayerStatsResults->ifaceId); |
| |
| if (NULL == pAdapter) |
| { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: vdev_id %d does not exist with host", |
| __func__, linkLayerStatsResults->ifaceId); |
| return; |
| } |
| |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| "%s: Link Layer Indication indType: %d", __func__, indType); |
| |
| switch (indType) |
| { |
| case SIR_HAL_LL_STATS_RESULTS_RSP: |
| { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "LL_STATS RESP paramID = 0x%x, ifaceId = %u respId = %u, moreResultToFollow = %u, num radio = %u result = %pK", |
| linkLayerStatsResults->paramId, linkLayerStatsResults->ifaceId, |
| linkLayerStatsResults->rspId, |
| linkLayerStatsResults->moreResultToFollow, |
| linkLayerStatsResults->num_radio, |
| linkLayerStatsResults->results); |
| |
| spin_lock(&hdd_context_lock); |
| context = &pHddCtx->ll_stats_context; |
| /* validate response received from target */ |
| if ((context->request_id != linkLayerStatsResults->rspId) || |
| !(context->request_bitmap & linkLayerStatsResults->paramId)) { |
| spin_unlock(&hdd_context_lock); |
| hddLog(LOGE, |
| FL("Error : Request id %d response id %d request bitmap 0x%x response bitmap 0x%x"), |
| context->request_id, linkLayerStatsResults->rspId, |
| context->request_bitmap, linkLayerStatsResults->paramId); |
| return; |
| } |
| spin_unlock(&hdd_context_lock); |
| |
| if (linkLayerStatsResults->paramId & WMI_LINK_STATS_RADIO ) |
| { |
| hdd_link_layer_process_radio_stats(pAdapter, |
| linkLayerStatsResults->moreResultToFollow, |
| (tpSirWifiRadioStat) |
| linkLayerStatsResults->results, |
| linkLayerStatsResults->num_radio); |
| |
| spin_lock(&hdd_context_lock); |
| if (!linkLayerStatsResults->moreResultToFollow) |
| context->request_bitmap &= ~(WMI_LINK_STATS_RADIO); |
| spin_unlock(&hdd_context_lock); |
| |
| } |
| else if (linkLayerStatsResults->paramId & WMI_LINK_STATS_IFACE ) |
| { |
| hdd_link_layer_process_iface_stats(pAdapter, |
| (tpSirWifiIfaceStat) |
| linkLayerStatsResults->results, |
| linkLayerStatsResults->num_peers); |
| |
| spin_lock(&hdd_context_lock); |
| /* Firmware doesn't send peerstats event if no peers are |
| * connected. HDD should not wait for any peerstats in this case |
| * and return the status to middlewre after receiving iface |
| * stats |
| */ |
| if (!linkLayerStatsResults->num_peers) |
| context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER); |
| context->request_bitmap &= ~(WMI_LINK_STATS_IFACE); |
| spin_unlock(&hdd_context_lock); |
| |
| } |
| else if (linkLayerStatsResults->paramId & WMI_LINK_STATS_ALL_PEER ) |
| { |
| hdd_link_layer_process_peer_stats(pAdapter, |
| linkLayerStatsResults->moreResultToFollow, |
| (tpSirWifiPeerStat) |
| linkLayerStatsResults->results); |
| |
| spin_lock(&hdd_context_lock); |
| if (!linkLayerStatsResults->moreResultToFollow) |
| context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER); |
| spin_unlock(&hdd_context_lock); |
| |
| } |
| else |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("INVALID LL_STATS_NOTIFY RESPONSE ***********")); |
| } |
| |
| spin_lock(&hdd_context_lock); |
| /* complete response event if all requests bitmaps are cleared */ |
| if (0 == context->request_bitmap) |
| complete(&context->response_event); |
| spin_unlock(&hdd_context_lock); |
| |
| break; |
| } |
| default: |
| hddLog(VOS_TRACE_LEVEL_ERROR, "invalid event type %d", indType); |
| break; |
| } |
| |
| return; |
| } |
| |
| static const struct nla_policy |
| qca_wlan_vendor_ll_ext_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR] = { |
| .type = NLA_U32 |
| }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF] = { |
| .type = NLA_U32 |
| }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters |
| * @wiphy: wiphy handle |
| * @wdev: wdev handle |
| * @data: user layer input |
| * @data_len: length of user layer input |
| * |
| * This function is called in SSR protected environment. |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int __wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int status; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1]; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct sir_ll_ext_stats_threshold thresh = {0,}; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO_LOW, |
| FL("Get user layer settings for LL stat. Length is %d bytes"), |
| data_len); |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX, |
| (struct nlattr *)data, data_len, |
| qca_wlan_vendor_ll_ext_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("maximum attribute not present")); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]) { |
| thresh.period = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]); |
| |
| if (thresh.period != 0 && thresh.period < LL_STATS_MIN_PERIOD) |
| thresh.period = LL_STATS_MIN_PERIOD; |
| |
| if (thresh.period > LL_STATS_MAX_PERIOD) |
| thresh.period = LL_STATS_MAX_PERIOD; |
| } else { |
| thresh.period = LL_STATS_INVALID_PERIOD; |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("No MAC counter period parameter")); |
| } |
| |
| /* period==0. Just disable mac counter */ |
| if (thresh.period == 0) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Mac counter will be disaabled")); |
| goto set_param; |
| } |
| |
| /* global thresh is not enabled */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]) { |
| thresh.global = false; |
| hddLog(VOS_TRACE_LEVEL_WARN, FL("Global thresh is not set")); |
| } else { |
| thresh.global_threshold = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]); |
| thresh.global = true; |
| hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("thresh is %d"), |
| thresh.global_threshold); |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]) { |
| thresh.global = false; |
| hddLog(VOS_TRACE_LEVEL_WARN, FL("Global thresh is not set")); |
| } else { |
| thresh.global = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]); |
| hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("Global is %d"), |
| thresh.global); |
| } |
| |
| thresh.enable_bitmap = false; |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]) { |
| thresh.tx_bitmap = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]); |
| thresh.enable_bitmap = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]) { |
| thresh.rx_bitmap = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]); |
| thresh.enable_bitmap = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]) { |
| thresh.cca_bitmap = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]); |
| thresh.enable_bitmap = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]) { |
| thresh.signal_bitmap = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]); |
| thresh.enable_bitmap = true; |
| } |
| |
| if (!thresh.global && !thresh.enable_bitmap) { |
| hddLog(VOS_TRACE_LEVEL_WARN, FL("No thresh exits.")); |
| thresh.enable = false; |
| } else { |
| thresh.enable = true; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]) { |
| thresh.tx.msdu = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]) { |
| thresh.tx.mpdu = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]) { |
| thresh.tx.ppdu = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]) { |
| thresh.tx.bytes = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]) { |
| thresh.tx.msdu_drop = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]) { |
| thresh.tx.byte_drop = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]) { |
| thresh.tx.mpdu_retry = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]) { |
| thresh.tx.mpdu_fail = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]) { |
| thresh.tx.ppdu_fail = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]) { |
| thresh.tx.aggregation = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]) { |
| thresh.tx.succ_mcs = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]) { |
| thresh.tx.fail_mcs = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]) { |
| thresh.tx.delay = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]) { |
| thresh.rx.mpdu = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]) { |
| thresh.rx.bytes = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]) { |
| thresh.rx.ppdu = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]) { |
| thresh.rx.ppdu_bytes = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]) { |
| thresh.rx.mpdu_lost = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]) { |
| thresh.rx.mpdu_retry = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]) { |
| thresh.rx.mpdu_dup = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]) { |
| thresh.rx.mpdu_discard = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]) { |
| thresh.rx.aggregation = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]) { |
| thresh.rx.mcs = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]) { |
| thresh.rx.ps_inds = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]) { |
| thresh.rx.ps_durs = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]) { |
| thresh.rx.probe_reqs = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]) { |
| thresh.rx.other_mgmt = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]) { |
| thresh.cca.idle_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]) { |
| thresh.cca.tx_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]) { |
| thresh.cca.rx_in_bss_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]) { |
| thresh.cca.rx_out_bss_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]) { |
| thresh.cca.rx_busy_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]) { |
| thresh.cca.rx_in_bad_cond_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]) { |
| thresh.cca.tx_in_bad_cond_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]) { |
| thresh.cca.wlan_not_avail_time = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]) { |
| thresh.signal.snr = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]) { |
| thresh.signal.nf = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]); |
| } |
| |
| set_param: |
| hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("Send thresh settings to target")); |
| if (eHAL_STATUS_SUCCESS != sme_ll_stats_set_thresh(hdd_ctx->hHal, |
| &thresh)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("sme_ll_stats_set_thresh failed.")); |
| return -EINVAL; |
| } |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters |
| * @wiphy: wiphy handle |
| * @wdev: wdev handle |
| * @data: user layer input |
| * @data_len: length of user layer input |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| int wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_ll_stats_ext_set_param(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * put_per_peer_ps_info() - put per peer sta's PS info into nl80211 msg |
| * @wifi_peer_info: peer information |
| * @vendor_event: buffer for vendor event |
| * |
| * Return: 0 success |
| */ |
| static int put_per_peer_ps_info(tSirWifiPeerInfo *wifi_peer_info, |
| struct sk_buff *vendor_event) |
| { |
| if (!wifi_peer_info) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Invalid pointer to peer info.")); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE, |
| wifi_peer_info->power_saving) || |
| nla_put(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, |
| VOS_MAC_ADDR_SIZE, wifi_peer_info->peerMacAddress)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * put_wifi_peer_ps_info() - Put peer sta's power state into nl80211 msg |
| * @data - stats for peer STA |
| * @vendor_event - buffer for vendor event |
| * |
| * Return: 0 success |
| */ |
| static int put_wifi_peer_ps_info(tSirWifiPeerStat *data, |
| struct sk_buff *vendor_event) |
| { |
| uint32_t peer_num, i; |
| tSirWifiPeerInfo *wifi_peer_info; |
| struct nlattr *peer_info, *peers; |
| |
| if (!data) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Invalid pointer to Wifi peer stat.")); |
| return -EINVAL; |
| } |
| |
| peer_num = data->numPeers; |
| |
| if (peer_num == 0) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Peer number is zero.")); |
| return -EINVAL; |
| } |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, |
| peer_num)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| peers = nla_nest_start(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG); |
| if (peers == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < peer_num; i++) { |
| wifi_peer_info = &data->peerInfo[i]; |
| peer_info = nla_nest_start(vendor_event, i); |
| |
| if (peer_info == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| if (put_per_peer_ps_info(wifi_peer_info, vendor_event)) |
| return -EINVAL; |
| |
| nla_nest_end(vendor_event, peer_info); |
| } |
| nla_nest_end(vendor_event, peers); |
| |
| return 0; |
| } |
| |
| /** |
| * put_tx_failure_info() - Put TX failure info |
| * @tx_fail - TX failure info |
| * @skb - buffer for vendor event |
| * |
| * Return: 0 Success |
| */ |
| static inline int put_tx_failure_info(struct sir_wifi_iface_tx_fail *tx_fail, |
| struct sk_buff *skb) |
| { |
| int status = 0; |
| |
| if (!tx_fail || !skb) |
| return -EINVAL; |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID, |
| tx_fail->tid) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU, |
| tx_fail->msdu_num) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS, |
| tx_fail->status)) { |
| hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| status = -EINVAL; |
| } |
| return status; |
| } |
| |
| /** |
| * put_wifi_channel_cca_info() - put channel cca info to vendor event |
| * @info: cca info array for all channels |
| * @vendor_event: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_channel_cca_info(struct sir_wifi_chan_cca_stats *cca, |
| struct sk_buff *vendor_event) |
| { |
| /* There might be no CCA info for a channel */ |
| if (cca == NULL) |
| return 0; |
| |
| if (nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME, |
| cca->idle_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME, |
| cca->tx_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME, |
| cca->rx_in_bss_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME, |
| cca->rx_out_bss_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY, |
| cca->rx_busy_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD, |
| cca->rx_in_bad_cond_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD, |
| cca->tx_in_bad_cond_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL, |
| cca->wlan_not_avail_time) || |
| nla_put_u32(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, |
| cca->vdev_id)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * put_wifi_signal_info - put chain signal info |
| * @info: RF chain signal info |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_signal_info(struct sir_wifi_peer_signal_stats *peer_signal, |
| struct sk_buff *skb) |
| { |
| uint32_t i, chain_count; |
| struct nlattr *chains, *att; |
| |
| /* There might be no signal info for a peer */ |
| if (peer_signal == NULL) |
| return 0; |
| |
| chain_count = peer_signal->num_chain < WIFI_MAX_CHAINS ? |
| peer_signal->num_chain : WIFI_MAX_CHAINS; |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM, |
| chain_count)) { |
| hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| att = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL); |
| if (att == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < chain_count; i++) { |
| chains = nla_nest_start(skb, i); |
| |
| if (chains == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG2, |
| FL("SNR=%d, NF=%d, RX=%d, TX=%d"), |
| peer_signal->per_ant_snr[i], |
| peer_signal->nf[i], |
| peer_signal->per_ant_rx_mpdus[i], |
| peer_signal->per_ant_tx_mpdus[i]); |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR, |
| peer_signal->per_ant_snr[i]) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF, |
| peer_signal->nf[i]) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, |
| peer_signal->per_ant_rx_mpdus[i]) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, |
| peer_signal->per_ant_tx_mpdus[i])) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| nla_nest_end(skb, chains); |
| } |
| nla_nest_end(skb, att); |
| |
| return 0; |
| } |
| |
| /** |
| * put_wifi_wmm_ac_tx_info() - put AC TX info |
| * @info: tx info |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_wmm_ac_tx_info(struct sir_wifi_tx *tx_stats, |
| struct sk_buff *skb) |
| { |
| uint32_t *agg_size, *succ_mcs, *fail_mcs, *delay; |
| |
| /* There might be no TX info for a peer */ |
| if (tx_stats == NULL) |
| return 0; |
| |
| agg_size = tx_stats->mpdu_aggr_size; |
| succ_mcs = tx_stats->success_mcs; |
| fail_mcs = tx_stats->fail_mcs; |
| delay = tx_stats->delay; |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU, |
| tx_stats->msdus) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU, |
| tx_stats->mpdus) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU, |
| tx_stats->ppdus) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES, |
| tx_stats->bytes) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP, |
| tx_stats->drops) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES, |
| tx_stats->drop_bytes) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY, |
| tx_stats->retries) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK, |
| tx_stats->failed) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM, |
| tx_stats->aggr_len) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM, |
| tx_stats->success_mcs_len) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM, |
| tx_stats->fail_mcs_len) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE, |
| tx_stats->delay_len)) |
| goto put_attr_fail; |
| |
| if (agg_size) { |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR, |
| tx_stats->aggr_len, agg_size)) |
| goto put_attr_fail; |
| } |
| |
| if (succ_mcs) { |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS, |
| tx_stats->success_mcs_len, succ_mcs)) |
| goto put_attr_fail; |
| } |
| |
| if (fail_mcs) { |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS, |
| tx_stats->fail_mcs_len, fail_mcs)) |
| goto put_attr_fail; |
| } |
| |
| if (delay) { |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY, |
| tx_stats->delay_len, delay)) |
| goto put_attr_fail; |
| } |
| return 0; |
| |
| put_attr_fail: |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| /** |
| * put_wifi_wmm_ac_rx_info() - put AC RX info |
| * @info: rx info |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_wmm_ac_rx_info(struct sir_wifi_rx *rx_stats, |
| struct sk_buff *skb) |
| { |
| uint32_t *mcs, *aggr; |
| |
| /* There might be no RX info for a peer */ |
| if (rx_stats == NULL) |
| return 0; |
| |
| aggr = rx_stats->mpdu_aggr; |
| mcs = rx_stats->mcs; |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU, |
| rx_stats->mpdus) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES, |
| rx_stats->bytes) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU, |
| rx_stats->ppdus) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES, |
| rx_stats->ppdu_bytes) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST, |
| rx_stats->mpdu_lost) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY, |
| rx_stats->mpdu_retry) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP, |
| rx_stats->mpdu_dup) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD, |
| rx_stats->mpdu_discard) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM, |
| rx_stats->aggr_len) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM, |
| rx_stats->mcs_len)) |
| goto put_attr_fail; |
| |
| if (aggr) { |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR, |
| rx_stats->aggr_len, aggr)) |
| goto put_attr_fail; |
| } |
| |
| if (mcs) { |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS, |
| rx_stats->mcs_len, mcs)) |
| goto put_attr_fail; |
| } |
| |
| return 0; |
| |
| put_attr_fail: |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| /** |
| * put_wifi_wmm_ac_info() - put WMM AC info |
| * @info: per AC stats |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_wmm_ac_info(struct sir_wifi_ll_ext_wmm_ac_stats *ac_stats, |
| struct sk_buff *skb) |
| { |
| struct nlattr *wmm; |
| |
| wmm = nla_nest_start(skb, ac_stats->type); |
| if (wmm == NULL) |
| goto nest_start_fail; |
| |
| if (put_wifi_wmm_ac_tx_info(ac_stats->tx_stats, skb) || |
| put_wifi_wmm_ac_rx_info(ac_stats->rx_stats, skb)) |
| goto put_attr_fail; |
| |
| nla_nest_end(skb, wmm); |
| return 0; |
| |
| nest_start_fail: |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| |
| put_attr_fail: |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| /** |
| * put_wifi_ll_ext_peer_info() - put per peer info |
| * @info: peer stats |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_ll_ext_peer_info(struct sir_wifi_ll_ext_peer_stats *peers, |
| struct sk_buff *skb) |
| { |
| uint32_t i; |
| struct nlattr *wmm_ac; |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID, |
| peers->peer_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID, |
| peers->vdev_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES, |
| peers->sta_ps_inds) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION, |
| peers->sta_ps_durs) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ, |
| peers->rx_probe_reqs) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT, |
| peers->rx_oth_mgmts) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS, |
| VOS_MAC_ADDR_SIZE, peers->mac_address) || |
| put_wifi_signal_info(&peers->peer_signal_stats, skb)) { |
| hddLog(LOGE, FL("put peer signal attr failed")); |
| return -EINVAL; |
| } |
| |
| wmm_ac = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS); |
| if (wmm_ac == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < WLAN_MAX_AC; i++) { |
| if (put_wifi_wmm_ac_info(&peers->ac_stats[i], skb)) { |
| hddLog(LOGE, FL("put WMM AC attr failed")); |
| return -EINVAL; |
| } |
| } |
| |
| nla_nest_end(skb, wmm_ac); |
| return 0; |
| } |
| |
| /** |
| * put_wifi_ll_ext_stats() - put link layer extension stats |
| * @info: link layer stats |
| * @skb: vendor event buffer |
| * |
| * Return: 0 Success, EINVAL failure |
| */ |
| static int put_wifi_ll_ext_stats(struct sir_wifi_ll_ext_stats *stats, |
| struct sk_buff *skb) |
| { |
| uint32_t i; |
| struct nlattr *peer, *peer_info, *channels, *channel_info; |
| struct sir_wifi_ll_ext_period *period; |
| |
| period = &stats->time_stamp; |
| if (hdd_wlan_nla_put_u64(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME, |
| period->end_time) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME, |
| period->duration) || |
| nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE, |
| stats->trigger_cond_id) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP, |
| stats->cca_chgd_bitmap) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP, |
| stats->sig_chgd_bitmap) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP, |
| stats->tx_chgd_bitmap) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP, |
| stats->rx_chgd_bitmap) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM, |
| stats->channel_num) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM, |
| stats->peer_num)) { |
| goto put_attr_fail; |
| } |
| |
| channels = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS); |
| if (channels == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < stats->channel_num; i++) { |
| channel_info = nla_nest_start(skb, i); |
| if (channel_info == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| if (put_wifi_channel_cca_info(&stats->cca[i], skb)) |
| goto put_attr_fail; |
| nla_nest_end(skb, channel_info); |
| } |
| nla_nest_end(skb, channels); |
| |
| peer_info = nla_nest_start(skb, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER); |
| if (peer_info == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < stats->peer_num; i++) { |
| peer = nla_nest_start(skb, i); |
| if (peer == NULL) { |
| hddLog(LOGE, FL("nla_nest_start failed")); |
| return -EINVAL; |
| } |
| |
| if (put_wifi_ll_ext_peer_info(&stats->peer_stats[i], skb)) |
| goto put_attr_fail; |
| nla_nest_end(skb, peer); |
| } |
| |
| nla_nest_end(skb, peer_info); |
| return 0; |
| |
| put_attr_fail: |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail")); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_ll_stats_ext_callback() - LL stats callback |
| * @rsp - msg from FW |
| * |
| * An extension of wlan_hdd_cfg80211_link_layer_stats_callback. |
| * It converts monitoring parameters offloaded to NL data and send the same |
| * to the kerbel/upper layer. |
| */ |
| static void wlan_hdd_cfg80211_ll_stats_ext_callback(tSirLLStatsResults *rsp) |
| { |
| hdd_context_t *hdd_ctx; |
| struct sk_buff *skb; |
| int flags = vos_get_gfp_flags(); |
| uint32_t param_id, index; |
| hdd_adapter_t *adapter; |
| tSirWifiPeerStat *peer_stats; |
| uint8_t *results; |
| int status, len; |
| v_CONTEXT_t vos_context = vos_get_global_context(0, NULL); |
| |
| ENTER(); |
| |
| if (!rsp) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid result.")); |
| return; |
| } |
| |
| hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_context); |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return; |
| |
| adapter = hdd_get_adapter_by_vdev(hdd_ctx, rsp->ifaceId); |
| if (NULL == adapter) { |
| hddLog(LOGE, FL("vdev_id %d does not exist with host"), |
| rsp->ifaceId); |
| return; |
| } |
| |
| index = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX; |
| len = LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN; |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL, len, |
| index, flags); |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); |
| return; |
| } |
| |
| results = rsp->results; |
| param_id = rsp->paramId; |
| hddLog(LOG1, |
| FL("LL_STATS RESP paramID = 0x%x, ifaceId = %u, result = %pK"), |
| rsp->paramId, rsp->ifaceId, rsp->results); |
| if (param_id & WMI_LL_STATS_EXT_PS_CHG) { |
| peer_stats = (tSirWifiPeerStat *)results; |
| status = put_wifi_peer_ps_info(peer_stats, skb); |
| } else if (param_id & WMI_LL_STATS_EXT_TX_FAIL) { |
| struct sir_wifi_iface_tx_fail *tx_fail; |
| |
| tx_fail = (struct sir_wifi_iface_tx_fail *)results; |
| status = put_tx_failure_info(tx_fail, skb); |
| } else if (param_id & WMI_LL_STATS_EXT_MAC_COUNTER) { |
| hddLog(LOGE, FL("MAC counters stats")); |
| status = put_wifi_ll_ext_stats((struct sir_wifi_ll_ext_stats *) |
| rsp->results, skb); |
| } else { |
| hddLog(LOGE, FL("Unknown link layer stats")); |
| status = -1; |
| } |
| |
| if (status == 0) |
| cfg80211_vendor_event(skb, flags); |
| else |
| kfree_skb(skb); |
| EXIT(); |
| } |
| |
| void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx) |
| { |
| sme_SetLinkLayerStatsIndCB(pHddCtx->hHal, |
| wlan_hdd_cfg80211_link_layer_stats_callback); |
| sme_set_ll_ext_cb(pHddCtx->hHal, |
| wlan_hdd_cfg80211_ll_stats_ext_callback); |
| } |
| |
| const struct |
| nla_policy |
| qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] = |
| { .type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] = |
| { .type = NLA_U32 }, |
| }; |
| |
| static int __wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int status; |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1]; |
| tSirLLStatsSetReq LinkLayerStatsSetReq; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX, |
| (struct nlattr *)data, |
| data_len, qca_wlan_vendor_ll_set_policy)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("maximum attribute not present")); |
| return -EINVAL; |
| } |
| |
| if (!tb_vendor |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("MPDU size Not present")); |
| return -EINVAL; |
| } |
| |
| if (!tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Stats Gathering Not Present")); |
| return -EINVAL; |
| } |
| |
| /* Shall take the request Id if the Upper layers pass. 1 For now.*/ |
| LinkLayerStatsSetReq.reqId = 1; |
| |
| LinkLayerStatsSetReq.mpduSizeThreshold = |
| nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]); |
| |
| LinkLayerStatsSetReq.aggressiveStatisticsGathering = |
| nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]); |
| |
| LinkLayerStatsSetReq.staId = pAdapter->sessionId; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "LL_STATS_SET reqId = %d, staId = %d, mpduSizeThreshold = %d Statistics Gathering = %d ", |
| LinkLayerStatsSetReq.reqId, LinkLayerStatsSetReq.staId, |
| LinkLayerStatsSetReq.mpduSizeThreshold, |
| LinkLayerStatsSetReq.aggressiveStatisticsGathering); |
| |
| |
| |
| if (eHAL_STATUS_SUCCESS != sme_LLStatsSetReq(pHddCtx->hHal, |
| &LinkLayerStatsSetReq)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s:" |
| "sme_LLStatsSetReq Failed", __func__); |
| return -EINVAL; |
| } |
| |
| pAdapter->isLinkLayerStatsSet = 1; |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_ll_stats_set() - set ll stats |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 if success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_ll_stats_set(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| const struct |
| nla_policy |
| qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX +1] = |
| { |
| /* Unsigned 32bit value provided by the caller issuing the GET stats |
| * command. When reporting |
| * the stats results, the driver uses the same value to indicate |
| * which GET request the results |
| * correspond to. |
| */ |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = { .type = NLA_U32 }, |
| |
| /* Unsigned 32bit value . bit mask to identify what statistics are |
| requested for retrieval */ |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = { .type = NLA_U32 } |
| }; |
| |
| static int __wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| unsigned long rc; |
| struct hdd_ll_stats_context *context; |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1]; |
| tSirLLStatsGetReq LinkLayerStatsGetReq; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_station_ctx_t *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter); |
| int status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| /* ENTER() intentionally not used in a frequently invoked API */ |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL ; |
| |
| if (!pAdapter->isLinkLayerStatsSet) { |
| hddLog(LOGW, FL("isLinkLayerStatsSet : %d"), |
| pAdapter->isLinkLayerStatsSet); |
| return -EINVAL; |
| } |
| |
| if (hddstactx->hdd_ReassocScenario) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Roaming in progress, so unable to proceed this request")); |
| return -EBUSY; |
| } |
| |
| if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX, |
| (struct nlattr *)data, |
| data_len, qca_wlan_vendor_ll_get_policy)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("max attribute not present")); |
| return -EINVAL; |
| } |
| |
| if (!tb_vendor |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request Id Not present")); |
| return -EINVAL; |
| } |
| |
| if (!tb_vendor |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Req Mask Not present")); |
| return -EINVAL; |
| } |
| |
| LinkLayerStatsGetReq.reqId = |
| nla_get_u32(tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]); |
| LinkLayerStatsGetReq.paramIdMask = |
| nla_get_u32(tb_vendor[ |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]); |
| |
| LinkLayerStatsGetReq.staId = pAdapter->sessionId; |
| |
| spin_lock(&hdd_context_lock); |
| context = &pHddCtx->ll_stats_context; |
| context->request_id = LinkLayerStatsGetReq.reqId; |
| context->request_bitmap = LinkLayerStatsGetReq.paramIdMask; |
| INIT_COMPLETION(context->response_event); |
| spin_unlock(&hdd_context_lock); |
| |
| if (eHAL_STATUS_SUCCESS != sme_LLStatsGetReq(pHddCtx->hHal, |
| &LinkLayerStatsGetReq)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s:" |
| "sme_LLStatsGetReq Failed", __func__); |
| return -EINVAL; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS)); |
| if (!rc) { |
| hddLog(LOGE, |
| FL("Target response timed out request id %d request bitmap 0x%x"), |
| context->request_id, context->request_bitmap); |
| return -ETIMEDOUT; |
| } |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_ll_stats_get() - get ll stats |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 if success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_ll_stats_get(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| const struct |
| nla_policy |
| qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8 }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8 }, |
| }; |
| |
| static int __wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1]; |
| tSirLLStatsClearReq LinkLayerStatsClearReq; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| u32 statsClearReqMask; |
| u8 stopReq; |
| int status; |
| struct sk_buff *temp_skbuff; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (!pAdapter->isLinkLayerStatsSet) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: isLinkLayerStatsSet : %d", |
| __func__, pAdapter->isLinkLayerStatsSet); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX, |
| (struct nlattr *)data, |
| data_len, qca_wlan_vendor_ll_clr_policy)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("STATS_CLR_MAX is not present")); |
| return -EINVAL; |
| } |
| |
| if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] || |
| !tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Error in LL_STATS CLR CONFIG PARA")); |
| return -EINVAL; |
| } |
| |
| statsClearReqMask = LinkLayerStatsClearReq.statsClearReqMask = |
| nla_get_u32( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]); |
| |
| stopReq = LinkLayerStatsClearReq.stopReq = |
| nla_get_u8( |
| tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]); |
| |
| /* |
| * Shall take the request Id if the Upper layers pass. 1 For now. |
| */ |
| LinkLayerStatsClearReq.reqId = 1; |
| |
| LinkLayerStatsClearReq.staId = pAdapter->sessionId; |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| "LL_STATS_CLEAR reqId = %d, staId = %d, statsClearReqMask = 0x%X, stopReq = %d", |
| LinkLayerStatsClearReq.reqId, |
| LinkLayerStatsClearReq.staId, |
| LinkLayerStatsClearReq.statsClearReqMask, |
| LinkLayerStatsClearReq.stopReq); |
| |
| |
| if (eHAL_STATUS_SUCCESS == sme_LLStatsClearReq(pHddCtx->hHal, |
| &LinkLayerStatsClearReq)) |
| { |
| temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| 2 * sizeof(u32) + |
| 2 * NLMSG_HDRLEN); |
| if (temp_skbuff != NULL) |
| { |
| if (nla_put_u32(temp_skbuff, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK, |
| statsClearReqMask) || |
| nla_put_u32(temp_skbuff, |
| QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP, |
| stopReq)) |
| { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("LL_STATS_CLR put fail")); |
| kfree_skb(temp_skbuff); |
| return -EINVAL; |
| } |
| |
| /* If the ask is to stop the stats collection as part of clear |
| * (stopReq = 1) , ensure that no further requests of get |
| * go to the firmware by having isLinkLayerStatsSet set to 0. |
| * However it the stopReq as part of the clear request is 0 , |
| * the request to get the statistics are honoured as in this |
| * case the firmware is just asked to clear the statistics. |
| */ |
| if (stopReq == 1) |
| pAdapter->isLinkLayerStatsSet = 0; |
| |
| return cfg80211_vendor_cmd_reply(temp_skbuff); |
| } |
| EXIT(); |
| return -ENOMEM; |
| } |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_ll_stats_clear() - clear ll stats |
| * @wiphy: Pointer to wiphy |
| * @wdev: Pointer to wdev |
| * @data: Pointer to data |
| * @data_len: Data length |
| * |
| * Return: 0 if success, non-zero for failure |
| */ |
| static int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_ll_stats_clear(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_clear_link_layer_stats() - clear link layer stats |
| * @adapter: pointer to adapter |
| * |
| * Wrapper function to clear link layer stats. |
| * return - void |
| */ |
| void wlan_hdd_clear_link_layer_stats(hdd_adapter_t *adapter) |
| { |
| tSirLLStatsClearReq link_layer_stats_clear_req; |
| tHalHandle hal = WLAN_HDD_GET_HAL_CTX(adapter); |
| |
| link_layer_stats_clear_req.statsClearReqMask = WIFI_STATS_IFACE_AC | |
| WIFI_STATS_IFACE_ALL_PEER; |
| link_layer_stats_clear_req.stopReq = 0; |
| link_layer_stats_clear_req.reqId = 1; |
| link_layer_stats_clear_req.staId = adapter->sessionId; |
| sme_LLStatsClearReq(hal, &link_layer_stats_clear_req); |
| |
| return; |
| } |
| |
| #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ |
| |
| #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) |
| { |
| uint8_t local_pmk[SIR_ROAM_SCAN_PSK_SIZE]; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *hdd_adapter_ptr = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx_ptr; |
| int status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if ((data == NULL) || (data_len == 0) || |
| (data_len > SIR_ROAM_SCAN_PSK_SIZE)) { |
| hddLog(LOGE, FL("Invalid data")); |
| return -EINVAL; |
| } |
| |
| hdd_ctx_ptr = WLAN_HDD_GET_CTX(hdd_adapter_ptr); |
| if (!hdd_ctx_ptr) { |
| hddLog(LOGE, FL("HDD context is null")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx_ptr); |
| if (0 != status) |
| return status; |
| |
| sme_UpdateRoamKeyMgmtOffloadEnabled(hdd_ctx_ptr->hHal, |
| hdd_adapter_ptr->sessionId, |
| TRUE); |
| vos_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE); |
| vos_mem_copy(local_pmk, data, data_len); |
| sme_RoamSetPSK_PMK(WLAN_HDD_GET_HAL_CTX(hdd_adapter_ptr), |
| hdd_adapter_ptr->sessionId, local_pmk, data_len); |
| 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_keymgmt_set_key(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_send_roam_auth_event() - Send the roamed and authorized event |
| * @hdd_ctx_ptr: pointer to HDD Context. |
| * @bssid: pointer to bssid of roamed AP. |
| * @req_rsn_ie: Pointer to request RSN IE |
| * @req_rsn_len: Length of the request RSN IE |
| * @rsp_rsn_ie: Pointer to response RSN IE |
| * @rsp_rsn_len: Length of the response RSN IE |
| * @roam_info_ptr: Pointer to the roaming related information |
| * |
| * This is called when wlan driver needs to send the roaming and |
| * authorization information after roaming. |
| * |
| * The information that would be sent is the request RSN IE, response |
| * RSN IE and BSSID of the newly roamed AP. |
| * |
| * If the Authorized status is authenticated, then additional parameters |
| * like PTK's KCK and KEK and Replay Counter would also be passed to the |
| * supplicant. |
| * |
| * The supplicant upon receiving this event would ignore the legacy |
| * cfg80211_roamed call and use the entire information from this event. |
| * The cfg80211_roamed should still co-exist since the kernel will |
| * make use of the parameters even if the supplicant ignores it. |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| int wlan_hdd_send_roam_auth_event(hdd_context_t *hdd_ctx_ptr, uint8_t *bssid, |
| uint8_t *req_rsn_ie, uint32_t req_rsn_len, |
| uint8_t *rsp_rsn_ie, uint32_t rsp_rsn_len, |
| tCsrRoamInfo *roam_info_ptr) |
| { |
| struct sk_buff *skb = NULL; |
| ENTER(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx_ptr)) |
| return -EINVAL; |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx_ptr->wiphy, |
| NULL, |
| ETH_ALEN + req_rsn_len + rsp_rsn_len + |
| sizeof(uint8) + SIR_REPLAY_CTR_LEN + |
| SIR_KCK_KEY_LEN + SIR_KCK_KEY_LEN + |
| (7 * NLMSG_HDRLEN), |
| QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX, |
| GFP_KERNEL); |
| |
| if (!skb) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_event_alloc failed")); |
| return -EINVAL; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, |
| ETH_ALEN, bssid) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, |
| req_rsn_len, req_rsn_ie) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, |
| rsp_rsn_len, rsp_rsn_ie)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Auth Status = %d"), |
| roam_info_ptr->synchAuthStatus); |
| if (roam_info_ptr->synchAuthStatus == |
| CSR_ROAM_AUTH_STATUS_AUTHENTICATED) { |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Include Auth Params TLV's")); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, |
| TRUE) || |
| nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, |
| SIR_REPLAY_CTR_LEN, roam_info_ptr->replay_ctr) |
| || nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, |
| SIR_KCK_KEY_LEN, roam_info_ptr->kck) |
| || nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, |
| SIR_KEK_KEY_LEN, roam_info_ptr->kek)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| } else { |
| hddLog(VOS_TRACE_LEVEL_DEBUG, FL("No Auth Params TLV's")); |
| if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, |
| FALSE)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| } |
| |
| cfg80211_vendor_event(skb, GFP_KERNEL); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| #endif |
| |
| static 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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1]; |
| tSirVersionString driver_version; |
| tSirVersionString firmware_version; |
| uint32_t major_spid = 0, minor_spid = 0, siid = 0, crmid = 0; |
| int status; |
| struct sk_buff *reply_skb; |
| uint32_t skb_len = 0, count = 0; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, data, |
| data_len, qca_wlan_vendor_get_wifi_info_policy)) { |
| hddLog(LOGE, FL("WIFI_INFO_GET NL CMD parsing failed")); |
| return -EINVAL; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) { |
| hddLog(LOG1, FL("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]) { |
| hddLog(LOG1, FL("Rcvd req for FW version")); |
| hdd_get_fw_version(hdd_ctx, &major_spid, &minor_spid, &siid, |
| &crmid); |
| snprintf(firmware_version, sizeof(firmware_version), |
| "%d:%d:%d:%d", major_spid, minor_spid, siid, crmid); |
| skb_len += strlen(firmware_version) + 1; |
| count++; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) { |
| hddLog(LOG1, FL("Rcvd req for Radio Index")); |
| skb_len += sizeof(hdd_ctx->radio_index); |
| count++; |
| } |
| |
| if (count == 0) { |
| hddLog(LOGE, FL("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) |
| goto error_skb_fail; |
| |
| 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_skb_fail: |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| |
| error_nla_fail: |
| hddLog(LOGE, FL("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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_wifi_info(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| int status; |
| uint32_t features; |
| struct sk_buff *reply_skb = NULL; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| features = 0; |
| |
| features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED; |
| features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED; |
| features |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED; |
| |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN); |
| if (!reply_skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| hddLog(LOG1, FL("Supported logger features: 0x%0x"), features); |
| if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED, |
| features)) { |
| hddLog(LOGE, FL("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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #ifdef FEATURE_WLAN_TDLS |
| /* EXT TDLS */ |
| static const struct nla_policy |
| wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = { |
| .type = NLA_UNSPEC, |
| .len = HDD_MAC_ADDR_LEN}, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] = |
| {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type = NLA_S32 }, |
| |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = { |
| .type = NLA_UNSPEC, |
| .len = HDD_MAC_ADDR_LEN}, |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_tdls_config_state_change_policy[ |
| QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = { |
| .type = NLA_UNSPEC, |
| .len = HDD_MAC_ADDR_LEN}, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS] = |
| {.type = NLA_U32 }, |
| |
| }; |
| |
| static const struct nla_policy |
| wlan_hdd_tdls_config_get_status_policy[ |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = { |
| .type = NLA_UNSPEC, |
| .len = HDD_MAC_ADDR_LEN}, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL] = {.type = NLA_U32 }, |
| [QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS] |
| = {.type = NLA_U32 }, |
| |
| }; |
| static int __wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| uint8_t peer[6] = {0}; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1]; |
| eHalStatus ret; |
| tANI_U32 state; |
| tANI_S32 reason; |
| uint32_t global_operating_class = 0; |
| uint32_t channel = 0; |
| struct sk_buff *skb = NULL; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(pHddCtx); |
| if (0 != ret) |
| return -EINVAL; |
| if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) { |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX, |
| data, data_len, |
| wlan_hdd_tdls_config_get_status_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid attribute")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch mac address */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed")); |
| return -EINVAL; |
| } |
| |
| memcpy(peer, nla_data( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]), |
| sizeof(peer)); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer)); |
| |
| ret = wlan_hdd_tdls_get_status(pAdapter, peer, &global_operating_class, |
| &channel, &state, &reason); |
| |
| if (0 != ret) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("get status Failed")); |
| return -EINVAL; |
| } |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| 4 * sizeof(int32_t) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -EINVAL; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Reason %d Status %d class %d channel %d peer " MAC_ADDRESS_STR), |
| reason, state, global_operating_class, |
| channel, MAC_ADDR_ARRAY(peer)); |
| |
| if (nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE, |
| state) || |
| nla_put_s32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON, |
| reason) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS, |
| global_operating_class) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL, |
| channel)) { |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| |
| ret = cfg80211_vendor_cmd_reply(skb); |
| EXIT(); |
| return ret; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_exttdls_get_status() - get ext tdls status |
| * @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_exttdls_get_status(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_exttdls_get_status(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static int wlan_hdd_cfg80211_exttdls_callback(const tANI_U8* mac, |
| uint32_t global_operating_class, |
| uint32_t channel, |
| tANI_U32 state, |
| tANI_S32 reason, |
| void *ctx) |
| { |
| hdd_adapter_t* pAdapter = (hdd_adapter_t*)ctx; |
| hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter); |
| struct sk_buff *skb = NULL; |
| |
| ENTER(); |
| |
| if (wlan_hdd_validate_context(pHddCtx)) |
| return -EINVAL; |
| |
| if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) { |
| return -ENOTSUPP; |
| } |
| skb = cfg80211_vendor_event_alloc( |
| pHddCtx->wiphy, |
| NULL, |
| EXTTDLS_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX, |
| GFP_KERNEL); |
| |
| if (!skb) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("cfg80211_vendor_event_alloc failed")); |
| return -EINVAL; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("Reason %d Status %d class %d channel %d peer " MAC_ADDRESS_STR), |
| reason, state, global_operating_class, |
| channel, MAC_ADDR_ARRAY(mac)); |
| |
| if (nla_put(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR, |
| VOS_MAC_ADDR_SIZE, mac) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE, |
| state) || |
| nla_put_s32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON, |
| reason) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL, |
| channel) || |
| nla_put_u32(skb, |
| QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS, |
| global_operating_class) |
| ) { |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_event(skb, GFP_KERNEL); |
| EXIT(); |
| return (0); |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| static int __wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| uint8_t peer[6] = {0}; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + 1]; |
| eHalStatus status; |
| tdls_req_params_t pReqMsg = {0}; |
| int ret; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("TDLS External Control is not enabled")); |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX, |
| data, data_len, |
| wlan_hdd_tdls_config_enable_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch mac address */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed")); |
| return -EINVAL; |
| } |
| |
| memcpy(peer, nla_data( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]), |
| sizeof(peer)); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer)); |
| |
| /* Parse and fetch channel */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr channel failed")); |
| return -EINVAL; |
| } |
| pReqMsg.channel = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Channel Num (%d)"), pReqMsg.channel); |
| |
| /* Parse and fetch global operating class */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr operating class failed")); |
| return -EINVAL; |
| } |
| pReqMsg.global_operating_class = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Operating class (%d)"), |
| pReqMsg.global_operating_class); |
| |
| /* Parse and fetch latency ms */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr latency failed")); |
| return -EINVAL; |
| } |
| pReqMsg.max_latency_ms = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Latency (%d)"), |
| pReqMsg.max_latency_ms); |
| |
| /* Parse and fetch required bandwidth kbps */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bandwidth failed")); |
| return -EINVAL; |
| } |
| |
| pReqMsg.min_bandwidth_kbps = nla_get_s32( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("Bandwidth (%d)"), |
| pReqMsg.min_bandwidth_kbps); |
| |
| ret = wlan_hdd_tdls_extctrl_config_peer(pAdapter, |
| peer, |
| wlan_hdd_cfg80211_exttdls_callback, |
| pReqMsg.channel, |
| pReqMsg.max_latency_ms, |
| pReqMsg.global_operating_class, |
| pReqMsg.min_bandwidth_kbps); |
| EXIT(); |
| return ret; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_exttdls_enable() - enable ext tdls |
| * @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_exttdls_enable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_exttdls_enable(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static int __wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| u8 peer[6] = {0}; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + 1]; |
| eHalStatus status; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return -EINVAL; |
| if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) { |
| |
| return -ENOTSUPP; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX, |
| data, data_len, |
| wlan_hdd_tdls_config_disable_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| /* Parse and fetch mac address */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed")); |
| return -EINVAL; |
| } |
| |
| memcpy(peer, nla_data( |
| tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]), |
| sizeof(peer)); |
| hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer)); |
| |
| status = wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer); |
| EXIT(); |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_exttdls_disable() - disable ext tdls |
| * @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_exttdls_disable(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_exttdls_disable(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #endif |
| |
| |
| static 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_disable_dfs_chan_scan () - disable/enable DFS channels |
| * |
| * @pHddCtx: HDD context within host driver |
| * @pAdapter: Adapter pointer |
| * @no_dfs_flag: If TRUE, DFS channels cannot be used for scanning |
| * |
| * Loops through devices to see who is operating on DFS channels |
| * and then disables/enables DFS channels by calling SME API. |
| * Fails the disable request if any device is active on a DFS channel. |
| * |
| * Return: EOK or other error codes. |
| */ |
| |
| int wlan_hdd_disable_dfs_chan_scan(hdd_context_t *pHddCtx, |
| hdd_adapter_t *pAdapter, |
| u32 no_dfs_flag) |
| { |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| hdd_adapter_list_node_t *p_adapter_node = NULL, *p_next = NULL; |
| hdd_adapter_t *p_adapter; |
| VOS_STATUS vos_status; |
| hdd_ap_ctx_t *p_ap_ctx; |
| hdd_station_ctx_t *p_sta_ctx; |
| eHalStatus status; |
| int ret_val = -EPERM; |
| |
| if (no_dfs_flag == pHddCtx->cfg_ini->enableDFSChnlScan) { |
| if (no_dfs_flag) { |
| vos_status = hdd_get_front_adapter( pHddCtx, &p_adapter_node); |
| while ((NULL != p_adapter_node) && |
| (VOS_STATUS_SUCCESS == vos_status)) |
| { |
| p_adapter = p_adapter_node->pAdapter; |
| |
| if (WLAN_HDD_SOFTAP == p_adapter->device_mode) { |
| p_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(p_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 !!!!! */ |
| if (NV_CHANNEL_DFS == |
| vos_nv_getChannelEnabledState( |
| p_ap_ctx->operatingChannel)) { |
| hddLog(LOGE, FL("SAP running on DFS channel")); |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| if (WLAN_HDD_INFRA_STATION == p_adapter->device_mode) { |
| p_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(p_adapter); |
| |
| /* if STA is already connected on DFS channel, |
| do not disable scan on dfs channels */ |
| if (hdd_connIsConnected(p_sta_ctx) && |
| (NV_CHANNEL_DFS == |
| vos_nv_getChannelEnabledState( |
| p_sta_ctx->conn_info.operationChannel))) { |
| hddLog(LOGE, FL("client connected on DFS channel")); |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| vos_status = hdd_get_next_adapter(pHddCtx, p_adapter_node, |
| &p_next); |
| p_adapter_node = p_next; |
| } |
| } |
| |
| pHddCtx->cfg_ini->enableDFSChnlScan = !no_dfs_flag; |
| |
| hdd_abort_mac_scan_all_adapters(pHddCtx); |
| |
| /* call the SME API to tunnel down the new channel list |
| to the firmware */ |
| status = sme_handle_dfs_chan_scan(hHal, |
| pHddCtx->cfg_ini->enableDFSChnlScan); |
| |
| if (eHAL_STATUS_SUCCESS == status) { |
| ret_val = 0; |
| |
| /* Clear the SME scan cache also. Note that the clearing of scan |
| * results is independent of session; so no need to iterate over |
| * all sessions |
| */ |
| status = sme_ScanFlushResult(hHal, pAdapter->sessionId); |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EPERM; |
| } |
| } else { |
| hddLog(LOG1, FL(" the DFS flag has not changed")); |
| ret_val = 0; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendof 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 net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1]; |
| int ret_val = -EPERM; |
| u32 no_dfs_flag = 0; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if ((ret_val = wlan_hdd_validate_context(pHddCtx))) |
| return ret_val; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX, |
| data, data_len, |
| wlan_hdd_set_no_dfs_flag_config_policy)) { |
| hddLog(LOGE, FL("invalid attr")); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) { |
| hddLog(LOGE, FL("attr dfs flag failed")); |
| return -EINVAL; |
| } |
| |
| no_dfs_flag = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]); |
| |
| hddLog(LOG1, FL(" DFS flag = %d"), |
| no_dfs_flag); |
| |
| if (no_dfs_flag > 1) { |
| hddLog(LOGE, FL("invalid value of dfs flag")); |
| return -EINVAL; |
| } |
| |
| ret_val = wlan_hdd_disable_dfs_chan_scan(pHddCtx, pAdapter, no_dfs_flag); |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command |
| * |
| * @wiphy: wiphy device pointer |
| * @wdev: wireless device pointer |
| * @data: Vendof 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_disable_dfs_chan_scan(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * 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 |
| */ |
| |
| #ifdef WLAN_FEATURE_MBSSID |
| static int wlan_hdd_sap_cfg_dfs_override(hdd_adapter_t *adapter) |
| { |
| hdd_adapter_t *con_sap_adapter; |
| tsap_Config_t *sap_config, *con_sap_config; |
| int con_ch; |
| tHalHandle hHal; |
| |
| /* |
| * Check if STA is running in a concurrent channel |
| */ |
| hHal = WLAN_HDD_GET_HAL_CTX(adapter); |
| con_ch = sme_GetConcurrentOperationChannel(hHal); |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| |
| if (con_ch && vos_get_concurrency_mode() == VOS_STA_SAP) { |
| |
| hddLog(LOG1, FL("concurrent STA role running on channel %d"), |
| con_ch); |
| |
| /* |
| * There is a STA role running on the same card, in that case |
| * DFS channel cannot be used by concurrent SAP. |
| * Try to use the same channel as the STA to achieve SCC |
| */ |
| if (VOS_IS_DFS_CH(sap_config->channel)) { |
| hddLog(LOG1, |
| FL("SAP channel config overridden due to DFS channel not allowed in STA+SAP mode %d -> %d"), |
| sap_config->channel, con_ch); |
| sap_config->channel = con_ch; |
| } |
| } |
| |
| /* |
| * Check if AP+AP case, once primary AP chooses a DFS |
| * channel secondary AP should always follow primary APs channel |
| */ |
| if (!vos_concurrent_beaconing_sessions_running()) |
| return 0; |
| |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, true); |
| if (!con_sap_adapter) |
| return 0; |
| |
| con_sap_config = &con_sap_adapter->sessionCtx.ap.sapConfig; |
| con_ch = con_sap_adapter->sessionCtx.ap.operatingChannel; |
| |
| if (!VOS_IS_DFS_CH(con_ch)) |
| return 0; |
| |
| hddLog(LOGE, FL("Only SCC AP-AP DFS Permitted (ch=%d, con_ch=%d)"), |
| sap_config->channel, con_ch); |
| hddLog(LOG1, FL("Overriding guest AP's channel")); |
| sap_config->channel = con_ch; |
| |
| if (con_sap_config->acs_cfg.acs_mode == true) { |
| if (con_ch != con_sap_config->acs_cfg.pri_ch && |
| con_ch != con_sap_config->acs_cfg.ht_sec_ch) { |
| hddLog(LOGE, FL("Primary AP channel config error")); |
| hddLog(LOGE, FL("Operating ch: %d ACS ch: %d %d"), |
| con_ch, con_sap_config->acs_cfg.pri_ch, |
| con_sap_config->acs_cfg.ht_sec_ch); |
| 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.ch_list) |
| vos_mem_free(sap_config->acs_cfg.ch_list); |
| |
| vos_mem_copy(&sap_config->acs_cfg, |
| &con_sap_config->acs_cfg, |
| sizeof(struct sap_acs_cfg)); |
| sap_config->acs_cfg.ch_list = vos_mem_malloc( |
| sizeof(uint8_t) * |
| con_sap_config->acs_cfg.ch_list_count); |
| if (!sap_config->acs_cfg.ch_list) { |
| hddLog(LOGE, FL("ACS config alloc fail")); |
| return -ENOMEM; |
| } |
| |
| vos_mem_copy(sap_config->acs_cfg.ch_list, |
| con_sap_config->acs_cfg.ch_list, |
| con_sap_config->acs_cfg.ch_list_count); |
| |
| } else { |
| sap_config->acs_cfg.pri_ch = con_ch; |
| if (sap_config->acs_cfg.ch_width > eHT_CHANNEL_WIDTH_20MHZ) |
| sap_config->acs_cfg.ht_sec_ch = con_sap_config->sec_ch; |
| } |
| |
| return con_ch; |
| } |
| #else |
| static int wlan_hdd_sap_cfg_dfs_override(hdd_adapter_t *adapter) |
| { |
| return 0; |
| } |
| #endif |
| |
| |
| |
| static int wlan_hdd_config_acs(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter) |
| { |
| tsap_Config_t *sap_config; |
| hdd_config_t *ini_config; |
| tHalHandle hal; |
| |
| hal = WLAN_HDD_GET_HAL_CTX(adapter); |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| ini_config = hdd_ctx->cfg_ini; |
| |
| sap_config->enOverLapCh = !!hdd_ctx->cfg_ini->gEnableOverLapCh; |
| #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE |
| hddLog(LOG1, FL("HDD_ACS_SKIP_STATUS = %d"), hdd_ctx->skip_acs_scan_status); |
| |
| if (hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) { |
| hdd_adapter_t *con_sap_adapter; |
| tsap_Config_t *con_sap_config = NULL; |
| |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, false); |
| |
| if (con_sap_adapter) |
| con_sap_config = &con_sap_adapter->sessionCtx.ap.sapConfig; |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN; |
| |
| if (con_sap_config && con_sap_config->acs_cfg.acs_mode == true && |
| hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) { |
| |
| if (con_sap_config->acs_cfg.hw_mode == sap_config->acs_cfg.hw_mode) { |
| v_U8_t con_sap_st_ch, con_sap_end_ch; |
| v_U8_t cur_sap_st_ch, cur_sap_end_ch; |
| v_U8_t bandStartChannel, bandEndChannel; |
| |
| con_sap_st_ch = con_sap_config->acs_cfg.start_ch; |
| con_sap_end_ch = con_sap_config->acs_cfg.end_ch; |
| cur_sap_st_ch = sap_config->acs_cfg.start_ch; |
| cur_sap_end_ch = sap_config->acs_cfg.end_ch; |
| |
| WLANSAP_extend_to_acs_range(&cur_sap_st_ch, &cur_sap_end_ch, |
| &bandStartChannel, &bandEndChannel); |
| |
| WLANSAP_extend_to_acs_range(&con_sap_st_ch, &con_sap_end_ch, |
| &bandStartChannel, &bandEndChannel); |
| |
| if (con_sap_st_ch <= cur_sap_st_ch && |
| con_sap_end_ch >= cur_sap_end_ch) { |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_SKIP_ACS_SCAN; |
| |
| } else if (con_sap_st_ch >= cur_sap_st_ch && |
| con_sap_end_ch >= cur_sap_end_ch) { |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = cur_sap_st_ch; |
| sap_config->acs_cfg.skip_scan_range1_endch = |
| con_sap_st_ch - 1; |
| sap_config->acs_cfg.skip_scan_range2_stch = 0; |
| sap_config->acs_cfg.skip_scan_range2_endch = 0; |
| |
| } else if (con_sap_st_ch <= cur_sap_st_ch && |
| con_sap_end_ch <= cur_sap_end_ch) { |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = |
| con_sap_end_ch + 1; |
| sap_config->acs_cfg.skip_scan_range1_endch = cur_sap_end_ch; |
| sap_config->acs_cfg.skip_scan_range2_stch = 0; |
| sap_config->acs_cfg.skip_scan_range2_endch = 0; |
| |
| } else if (con_sap_st_ch >= cur_sap_st_ch && |
| con_sap_end_ch <= cur_sap_end_ch) { |
| |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN; |
| |
| sap_config->acs_cfg.skip_scan_range1_stch = cur_sap_st_ch; |
| sap_config->acs_cfg.skip_scan_range1_endch = |
| con_sap_st_ch - 1; |
| sap_config->acs_cfg.skip_scan_range2_stch = con_sap_end_ch; |
| sap_config->acs_cfg.skip_scan_range2_endch = |
| cur_sap_end_ch + 1; |
| |
| } else |
| sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN; |
| } |
| hddLog(LOG1, |
| FL("SecAP ACS Skip = %d, ACS CH RANGE = %d-%d, %d-%d"), |
| sap_config->acs_cfg.skip_scan_status, |
| sap_config->acs_cfg.skip_scan_range1_stch, |
| sap_config->acs_cfg.skip_scan_range1_endch, |
| sap_config->acs_cfg.skip_scan_range2_stch, |
| sap_config->acs_cfg.skip_scan_range2_endch); |
| } |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_set_acs_ch_range : Start ACS channel range values |
| * @sap_cfg: pointer to SAP config struct |
| * |
| * This function sets the default ACS start and end channel for the given band |
| * and also parses the given ACS channel list. |
| * |
| * Return: None |
| */ |
| |
| static void wlan_hdd_set_acs_ch_range(tsap_Config_t *sap_cfg, bool ht_enabled, |
| bool vht_enabled) |
| { |
| int i; |
| if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211B) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11b; |
| sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_1].channelNum; |
| sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_14].channelNum; |
| sap_cfg->target_band = eCSR_BAND_24; |
| } else if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211G) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11g; |
| sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_1].channelNum; |
| sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_13].channelNum; |
| sap_cfg->target_band = eCSR_BAND_24; |
| } else if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211A) { |
| sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11a; |
| sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_36].channelNum; |
| sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_165].channelNum; |
| sap_cfg->target_band = eCSR_BAND_5G; |
| } else { |
| hddLog(LOG1, FL("hw_mode %d"), sap_cfg->acs_cfg.hw_mode); |
| sap_cfg->target_band = eCSR_BAND_5G; |
| } |
| 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.ch_list) |
| return; |
| |
| sap_cfg->acs_cfg.start_ch = sap_cfg->acs_cfg.ch_list[0]; |
| sap_cfg->acs_cfg.end_ch = |
| sap_cfg->acs_cfg.ch_list[sap_cfg->acs_cfg.ch_list_count - 1]; |
| for (i = 0; i < sap_cfg->acs_cfg.ch_list_count; i++) { |
| /* avoid channel 0 as start channel */ |
| if (sap_cfg->acs_cfg.start_ch > sap_cfg->acs_cfg.ch_list[i] && |
| sap_cfg->acs_cfg.ch_list[i] != 0 ) |
| sap_cfg->acs_cfg.start_ch = sap_cfg->acs_cfg.ch_list[i]; |
| if (sap_cfg->acs_cfg.end_ch < sap_cfg->acs_cfg.ch_list[i]) |
| sap_cfg->acs_cfg.end_ch = sap_cfg->acs_cfg.ch_list[i]; |
| } |
| |
| } |
| |
| |
| static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work); |
| |
| /** |
| * 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 |
| */ |
| |
| static int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter) |
| { |
| |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| tsap_Config_t *sap_config; |
| tpWLAN_SAPEventCB acs_event_callback; |
| int status; |
| |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) |
| sap_config->backup_channel = sap_config->channel; |
| |
| if (hdd_ctx->acs_policy.acs_channel) |
| sap_config->channel = hdd_ctx->acs_policy.acs_channel; |
| else |
| sap_config->channel = AUTO_CHANNEL_SELECT; |
| status = wlan_hdd_sap_cfg_dfs_override(adapter); |
| if (status < 0) { |
| return status; |
| } else { |
| if (status > 0) { |
| /* notify hostapd about channel override */ |
| wlan_hdd_cfg80211_acs_ch_select_evt(adapter); |
| clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags); |
| return 0; |
| } |
| } |
| |
| status = wlan_hdd_config_acs(hdd_ctx, adapter); |
| if (status) { |
| hddLog(LOGE, FL("ACS config failed")); |
| return -EINVAL; |
| } |
| |
| acs_event_callback = hdd_hostapd_SAPEventCB; |
| |
| vos_mem_copy(sap_config->self_macaddr.bytes, |
| adapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t)); |
| hddLog(LOG1, FL("ACS Started for wlan%d"), adapter->dev->ifindex); |
| status = WLANSAP_ACS_CHSelect( |
| #ifdef WLAN_FEATURE_MBSSID |
| WLAN_HDD_GET_SAP_CTX_PTR(adapter), |
| #else |
| hdd_ctx->pvosContext, |
| #endif |
| acs_event_callback, sap_config, (v_PVOID_t)adapter->dev); |
| |
| |
| if (status) { |
| hddLog(LOGE, FL("ACS channel select failed")); |
| return -EINVAL; |
| } |
| sap_config->acs_cfg.acs_mode = true; |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| if (is_auto_channel_select(WLAN_HDD_GET_SAP_CTX_PTR(adapter))) |
| #endif |
| set_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags); |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_relaunch_acs() - Relaunch ACS for SAP |
| * @adapter: pointer to SAP adapter structure |
| * |
| * This function relaunches ACS. |
| * |
| * Return: Result status of ACS relaunch |
| */ |
| |
| static int wlan_hdd_cfg80211_relaunch_acs(hdd_adapter_t *adapter) |
| { |
| uint8_t channel_list[MAX_CHANNEL] = {0}; |
| uint8_t number_of_channels = 0; |
| hdd_context_t *hdd_ctx = (hdd_context_t *)adapter->pHddCtx; |
| tsap_Config_t *sap_config; |
| int status; |
| uint8_t cur_band, i; |
| uint8_t band_start_channel; |
| uint8_t band_end_channel; |
| bool ht_enabled = false, vht_enabled = false; |
| |
| if (!test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| hddLog(LOGE, FL("Softap is not started")); |
| return -EINVAL; |
| } |
| |
| cur_band = vos_chan_to_band((WLAN_HDD_GET_AP_CTX_PTR( |
| adapter))->operatingChannel); |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| vos_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg)); |
| |
| if (VOS_BAND_2GHZ == cur_band) { |
| band_start_channel = RF_CHAN_1; |
| band_end_channel = RF_CHAN_14; |
| } else { |
| band_start_channel = RF_CHAN_36; |
| band_end_channel = RF_CHAN_165; |
| } |
| |
| for (i = band_start_channel; i <= band_end_channel; i++) { |
| if (NV_CHANNEL_ENABLE == |
| vos_nv_getChannelEnabledState( |
| rfChannels[i].channelNum)) { |
| channel_list[number_of_channels++] = |
| rfChannels[i].channelNum; |
| hddLog(LOG1, |
| FL("Config acs channel[%d]"), |
| rfChannels[i].channelNum); |
| } |
| } |
| |
| if (sap_config->SapHw_mode & |
| (eCSR_DOT11_MODE_11n | |
| eCSR_DOT11_MODE_11n_ONLY | |
| eCSR_DOT11_MODE_11ac)) |
| ht_enabled = true; |
| |
| if (sap_config->SapHw_mode & |
| (eCSR_DOT11_MODE_11ac | |
| eCSR_DOT11_MODE_11ac_ONLY)) |
| vht_enabled = true; |
| |
| sap_config->acs_cfg.ch_width = sap_config->ch_width_orig; |
| |
| hddLog(LOG1, |
| FL("ht_enabled=%d vht_enabled=%d ch_width=%d"), |
| ht_enabled, |
| vht_enabled, |
| sap_config->acs_cfg.ch_width); |
| |
| wlan_hdd_set_acs_ch_range(sap_config, ht_enabled, vht_enabled); |
| |
| sap_config->acsBandSwitchThreshold = |
| hdd_ctx->cfg_ini->acsBandSwitchThreshold; |
| |
| if (hdd_ctx->cfg_ini->auto_channel_select_weight) |
| sap_config->auto_channel_select_weight = |
| hdd_ctx->cfg_ini->auto_channel_select_weight; |
| |
| sap_config->acs_cfg.acs_mode = true; |
| if (test_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags)) { |
| hddLog(LOG1, FL("ACS Pending for wlan")); |
| status = -EINVAL; |
| } else { |
| hddLog(LOG1, FL("Relaunch ACS")); |
| wlan_hdd_cfg80211_start_acs(adapter); |
| status = 0; |
| } |
| |
| return status; |
| } |
| |
| #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH |
| /** |
| * wlan_hdd_set_mcc_to_scc_switch() - set mcc to scc switch mode from ini |
| * @adapter: hdd_adapter_t ptr of the interface |
| * |
| * This function updates ini WlanMccToSccSwitchMode value to adapter |
| * context. The value controls the MCC to SCC switch behavior. |
| * |
| * Return: void |
| */ |
| static void |
| wlan_hdd_set_mcc_to_scc_switch(hdd_adapter_t *adapter) |
| { |
| hdd_context_t * hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| hdd_config_t *cfg_ini = hdd_ctx->cfg_ini; |
| tsap_Config_t *sap_config; |
| |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| sap_config->cc_switch_mode = cfg_ini->WlanMccToSccSwitchMode; |
| sap_config->band_switch_enable = cfg_ini->wlan_band_switch_enable; |
| sap_config->ap_p2pclient_concur_enable = |
| cfg_ini->wlan_ap_p2pgo_conc_enable; |
| sap_config->ch_width_24g_orig = cfg_ini->nChannelBondingMode24GHz ? |
| eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ; |
| sap_config->ch_width_5g_orig = cfg_ini->vhtChannelWidth; |
| } |
| #else |
| static void |
| wlan_hdd_set_mcc_to_scc_switch(hdd_adapter_t *adapter) |
| {} |
| #endif |
| 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_UNSPEC }, |
| }; |
| |
| |
| /** |
| * __wlan_hdd_cfg80211_do_acs() : CFG80211 handler fucntion 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) |
| { |
| struct net_device *ndev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| tsap_Config_t *sap_config; |
| struct sk_buff *temp_skbuff; |
| int status = -EINVAL, i = 0; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; |
| bool ht_enabled, ht40_enabled, vht_enabled; |
| uint8_t ch_width; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (hdd_ctx->cfg_ini->force_sap_acs) { |
| hddLog(LOGE, FL("Hostapd ACS rejected as driver INI force ACS is enabled")); |
| return -EPERM; |
| } |
| |
| /* ***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 |
| * seperate 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. |
| */ |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return status; |
| |
| if (hdd_cfg_is_static_sub20_channel_width_enabled(hdd_ctx)) { |
| hddLog(LOGE, FL("ACS not support if static sub20 enable")); |
| status = -EINVAL; |
| goto out; |
| } |
| |
| if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) { |
| uint32_t sap_sub20_channelwidth; |
| WLANSAP_get_sub20_channelwidth(WLAN_HDD_GET_SAP_CTX_PTR( |
| adapter), |
| &sap_sub20_channelwidth); |
| if (sap_sub20_channelwidth == SUB20_MODE_NONE) { |
| hddLog(LOGE, FL("Bss started, relaunch ACS")); |
| status = wlan_hdd_cfg80211_relaunch_acs(adapter); |
| } else { |
| hddLog(LOGE, FL("ACS not support in sub20 enable")); |
| status = -EINVAL; |
| } |
| return status; |
| } |
| |
| sap_config = &adapter->sessionCtx.ap.sapConfig; |
| vos_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg)); |
| |
| status = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, data, data_len, |
| wlan_hdd_cfg80211_do_acs_policy); |
| if (status) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR")); |
| goto out; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Attr hw_mode failed")); |
| goto out; |
| } |
| sap_config->acs_cfg.hw_mode = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]); |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]) |
| ht_enabled = |
| nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]); |
| else |
| ht_enabled = 0; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]) |
| ht40_enabled = |
| nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]); |
| else |
| ht40_enabled = 0; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]) |
| vht_enabled = |
| nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]); |
| else |
| vht_enabled = 0; |
| |
| if (hdd_ctx->cfg_ini->sap_force_11n_for_11ac) { |
| vht_enabled = 0; |
| hddLog(LOG1, FL("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; |
| } |
| |
| /* this may be possible, when sap_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; |
| } |
| |
| if (ch_width == 80) |
| sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_80MHZ; |
| else if (ch_width == 40) |
| sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_40MHZ; |
| else |
| sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_20MHZ; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]) { |
| char *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.ch_list = vos_mem_malloc( |
| sizeof(uint8_t) * |
| sap_config->acs_cfg.ch_list_count); |
| if (sap_config->acs_cfg.ch_list == NULL) { |
| hddLog(LOGE, FL("ACS config alloc fail")); |
| status = -ENOMEM; |
| goto out; |
| } |
| vos_mem_copy(sap_config->acs_cfg.ch_list, tmp, |
| sap_config->acs_cfg.ch_list_count); |
| } |
| } |
| wlan_hdd_set_mcc_to_scc_switch(adapter); |
| wlan_hdd_set_acs_ch_range(sap_config, ht_enabled, vht_enabled); |
| |
| /* ACS override for android */ |
| if (hdd_ctx->cfg_ini->sap_p2p_11ac_override && ht_enabled && |
| !hdd_ctx->cfg_ini->sap_force_11n_for_11ac) { |
| hddLog(LOG1, FL("ACS Config override for 11AC")); |
| vht_enabled = 1; |
| sap_config->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac; |
| sap_config->acs_cfg.ch_width = |
| hdd_ctx->cfg_ini->vhtChannelWidth; |
| /* No VHT80 in 2.4G so perform ACS accordingly */ |
| if (sap_config->acs_cfg.end_ch <= 14 && |
| sap_config->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ) |
| sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_40MHZ; |
| } |
| |
| sap_config->acsBandSwitchThreshold = |
| hdd_ctx->cfg_ini->acsBandSwitchThreshold; |
| |
| if (hdd_ctx->cfg_ini->auto_channel_select_weight) |
| sap_config->auto_channel_select_weight = |
| hdd_ctx->cfg_ini->auto_channel_select_weight; |
| |
| hddLog(LOG1, FL("ACS Config for wlan%d: HW_MODE: %d ACS_BW: %d HT: %d VHT: %d START_CH: %d END_CH: %d"), |
| adapter->dev->ifindex, sap_config->acs_cfg.hw_mode, |
| ch_width, ht_enabled, vht_enabled, |
| sap_config->acs_cfg.start_ch, sap_config->acs_cfg.end_ch); |
| |
| if (sap_config->acs_cfg.ch_list_count) { |
| hddLog(LOG1, FL("ACS channel list: len: %d"), |
| sap_config->acs_cfg.ch_list_count); |
| for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) |
| hddLog(LOG1, "%d ", sap_config->acs_cfg.ch_list[i]); |
| } |
| sap_config->acs_cfg.acs_mode = true; |
| if (test_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags)) { |
| /* ***Note*** Completion variable usage is not allowed here since |
| * ACS scan operation may take max 2.2 sec for 5G band. |
| * 9 Active channel X 40 ms active scan time + |
| * 16 Passive channel X 110ms passive scan time |
| * Since this CFG80211 call lock rtnl mutex, we cannot hold on |
| * for this long. So we split up the scanning part. |
| */ |
| set_bit(ACS_PENDING, &adapter->event_flags); |
| hddLog(LOG1, FL("ACS Pending for wlan%d"), |
| adapter->dev->ifindex); |
| status = 0; |
| } else { |
| status = wlan_hdd_cfg80211_start_acs(adapter); |
| } |
| |
| out: |
| if (0 == status) { |
| temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, |
| NLMSG_HDRLEN); |
| if (temp_skbuff != NULL) |
| return cfg80211_vendor_cmd_reply(temp_skbuff); |
| } |
| wlan_hdd_undo_acs(adapter); |
| clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags); |
| |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_do_acs : CFG80211 handler fucntion 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_do_acs(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * 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(hdd_adapter_t *adapter) |
| { |
| if (adapter == NULL) |
| return; |
| if (adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list) { |
| vos_mem_free(adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list); |
| adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list = NULL; |
| } |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_start_pending_acs : Start pending ACS procedure for SAP |
| * @work: Linux workqueue struct pointer for ACS work |
| * |
| * This function starts the ACS procedure which was marked pending when an ACS |
| * procedure was in progress for a concurrent SAP interface. |
| * |
| * Return: None |
| */ |
| |
| static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work) |
| { |
| hdd_adapter_t *adapter = container_of(work, hdd_adapter_t, |
| acs_pending_work.work); |
| wlan_hdd_cfg80211_start_acs(adapter); |
| } |
| |
| /** |
| * 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 from SAP module 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(hdd_adapter_t *adapter) |
| { |
| hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter); |
| tsap_Config_t *sap_cfg = &(WLAN_HDD_GET_AP_CTX_PTR(adapter))->sapConfig; |
| struct sk_buff *vendor_event; |
| int ret_val; |
| struct nlattr *nla; |
| hdd_adapter_t *con_sap_adapter; |
| uint16_t ch_width; |
| |
| vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, |
| 4 * sizeof(u8) + 1 * sizeof(u16) + 4 + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX, |
| GFP_KERNEL); |
| |
| if (!vendor_event) { |
| hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); |
| return; |
| } |
| |
| /* Send the IF INDEX to differentiate the ACS event for each interface |
| * TODO: To be update once cfg80211 APIs are updated to accept if_index |
| */ |
| nla_nest_cancel(vendor_event, ((void **)vendor_event->cb)[2]); |
| |
| ret_val = nla_put_u32(vendor_event, NL80211_ATTR_IFINDEX, |
| adapter->dev->ifindex); |
| if (ret_val) { |
| hddLog(LOGE, FL("NL80211_ATTR_IFINDEX put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| nla = nla_nest_start(vendor_event, NL80211_ATTR_VENDOR_DATA); |
| ((void **)vendor_event->cb)[2] = nla; |
| |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, |
| sap_cfg->acs_cfg.pri_ch); |
| if (ret_val) { |
| hddLog(LOGE, |
| FL("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, |
| sap_cfg->acs_cfg.ht_sec_ch); |
| if (ret_val) { |
| hddLog(LOGE, |
| FL( |
| "QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL, |
| sap_cfg->acs_cfg.vht_seg0_center_ch); |
| if (ret_val) { |
| hddLog(LOGE, |
| FL( |
| "QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| ret_val = nla_put_u8(vendor_event, |
| QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL, |
| sap_cfg->acs_cfg.vht_seg1_center_ch); |
| if (ret_val) { |
| hddLog(LOGE, |
| FL( |
| "QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| if (sap_cfg->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ) |
| ch_width = 80; |
| else if (sap_cfg->acs_cfg.ch_width == eHT_CHANNEL_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) { |
| hddLog(LOGE, |
| FL( |
| "QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH put fail")); |
| kfree_skb(vendor_event); |
| return; |
| } |
| |
| hddLog(LOG1, |
| FL("ACS result for wlan%d: PRI_CH: %d SEC_CH: %d VHT_SEG0: %d VHT_SEG1: %d ACS_BW: %d"), |
| adapter->dev->ifindex, sap_cfg->acs_cfg.pri_ch, |
| sap_cfg->acs_cfg.ht_sec_ch,sap_cfg->acs_cfg.vht_seg0_center_ch, |
| sap_cfg->acs_cfg.vht_seg1_center_ch, ch_width); |
| |
| cfg80211_vendor_event(vendor_event, GFP_KERNEL); |
| /* ***Note*** As already mentioned Completion variable usage is not |
| * allowed here since ACS scan operation may take max 2.2 sec. |
| * Further in AP-AP mode pending ACS is resumed here to serailize ACS |
| * operation. |
| * TODO: Delayed operation is used since SME-PMAC strut is global. Thus |
| * when Primary AP ACS is complete and secondary AP ACS is started here |
| * immediately, Primary AP start_bss may come inbetween ACS operation |
| * and overwrite Sec AP ACS paramters. Thus Sec AP ACS is executed with |
| * delay. This path and below constraint will be removed on sessionizing |
| * SAP acs parameters and decoupling SAP from PMAC (WIP). |
| * As per design constraint user space control application must take |
| * care of serailizing hostapd start for each VIF in AP-AP mode to avoid |
| * this code path. Sec AP hostapd should be started after Primary AP |
| * start beaconing which can be confirmed by getchannel iwpriv command |
| */ |
| |
| con_sap_adapter = hdd_get_con_sap_adapter(adapter, false); |
| if (con_sap_adapter && |
| test_bit(ACS_PENDING, &con_sap_adapter->event_flags)) { |
| #ifdef CONFIG_CNSS |
| cnss_init_delayed_work(&con_sap_adapter->acs_pending_work, |
| wlan_hdd_cfg80211_start_pending_acs); |
| #else |
| INIT_DELAYED_WORK(&con_sap_adapter->acs_pending_work, |
| wlan_hdd_cfg80211_start_pending_acs); |
| #endif |
| /* Lets give 500ms for OBSS + START_BSS to complete */ |
| schedule_delayed_work(&con_sap_adapter->acs_pending_work, |
| msecs_to_jiffies(500)); |
| clear_bit(ACS_PENDING, &con_sap_adapter->event_flags); |
| } |
| |
| return; |
| } |
| |
| static const struct nla_policy |
| wlan_hdd_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX |
| +1] = |
| { |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM] = {.type = NLA_U32 }, |
| [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_TX_RATE] = {.type = NLA_U16 }, |
| [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_TX_FAIL_COUNT] = {.type = NLA_U32 }, |
| [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_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] = { |
| .type = NLA_UNSPEC}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT] = { |
| .type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH] = {.type = NLA_U32}, |
| [QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY] = {.type = NLA_U32}, |
| }; |
| |
| /** |
| * wlan_hdd_update_tx_rate() - update tx rate to firmware |
| * @hdd_ctx: HDD context |
| * @tx_rate: User-specified tx-rate to be operated for the specific |
| * HW mode. |
| * Return: 0 on success; Errno on failure |
| */ |
| int wlan_hdd_update_tx_rate(hdd_context_t *hddctx, uint16_t tx_rate) |
| { |
| |
| hdd_adapter_t *adapter; |
| hdd_station_ctx_t *hddstactx; |
| eHalStatus hstatus; |
| struct sir_txrate_update *buf_txrate_update; |
| |
| ENTER(); |
| adapter = hdd_get_adapter(hddctx, WLAN_HDD_INFRA_STATION); |
| if (!adapter) { |
| hddLog(LOGE, FL("hdd adapter is null")); |
| return -EINVAL; |
| } |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hddLog(LOGE, FL("hdd adapter cookie is invalid")); |
| return -EINVAL; |
| } |
| |
| if (WLAN_HDD_INFRA_STATION != adapter->device_mode) { |
| hddLog(LOGE, FL("Only Sta Mode supported!")); |
| return -ENOTSUPP; |
| } |
| |
| hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (!hdd_connIsConnected(hddstactx)) { |
| hddLog(LOGE, FL("Not in Connected state!")); |
| return -ENOTSUPP; |
| } |
| |
| buf_txrate_update = vos_mem_malloc(sizeof(*buf_txrate_update)); |
| if (!buf_txrate_update) { |
| hddLog(LOGE, FL("Failed to allocate memory for buf_txrate_update")); |
| return -ENOMEM; |
| } |
| |
| |
| buf_txrate_update->session_id = adapter->sessionId; |
| |
| buf_txrate_update->txrate = tx_rate; |
| vos_mem_copy(buf_txrate_update->bssid, hddstactx->conn_info.bssId, |
| VOS_MAC_ADDR_SIZE); |
| |
| hstatus = sme_update_txrate(hddctx->hHal, buf_txrate_update); |
| if (!HAL_STATUS_SUCCESS(hstatus)) { |
| hddLog(LOGE, |
| FL("sme_update_txrate failed(err=%d)"), hstatus); |
| vos_mem_free(buf_txrate_update); |
| return -EINVAL; |
| } |
| EXIT(); |
| vos_mem_free(buf_txrate_update); |
| return 0; |
| } |
| |
| /** |
| * hdd_set_qpower_config() - set qpower config to firmware |
| * @adapter: HDD adapter |
| * @qpower: new qpower config value |
| * |
| * Return: 0 on success; Errno on failure |
| */ |
| static int hdd_set_qpower_config(hdd_context_t *hddctx, hdd_adapter_t *adapter, |
| uint8_t qpower) |
| { |
| VOS_STATUS vos_status; |
| |
| if(!adapter) { |
| hddLog(LOGE,"invalid adapter"); |
| return -EINVAL; |
| } |
| if (adapter->device_mode != WLAN_HDD_INFRA_STATION && |
| adapter->device_mode != WLAN_HDD_P2P_CLIENT) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("QPOWER is only supported for STA/P2P-CLIENT")); |
| return -EINVAL; |
| } |
| if (!hddctx->cfg_ini->enablePowersaveOffload) { |
| hddLog(LOGE, |
| FL("qpower is disabled in configuration")); |
| return -EINVAL; |
| } |
| if (qpower > PS_DUTY_CYCLING_QPOWER || |
| qpower < PS_LEGACY_NODEEPSLEEP) { |
| hddLog(LOGE, |
| FL("invalid qpower value=%d"), qpower); |
| return -EINVAL; |
| } |
| vos_status = wma_set_powersave_config(adapter->sessionId, qpower); |
| if (vos_status != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, |
| FL("failed to update qpower %d"), |
| vos_status); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * hdd_sta_set_sub20_channelwidth() - |
| * This api function does a sub20 channel width change |
| * for STA while STA in disconnect state |
| * @adapter: HDD adapter |
| * @chan_width: New channel width change to |
| * |
| * Return: The VOS_STATUS code associated with performing |
| * the operation |
| */ |
| static VOS_STATUS |
| hdd_sta_set_sub20_channelwidth(hdd_adapter_t *adapter, uint32_t chan_width) |
| { |
| tHalHandle hal_ptr; |
| |
| hddLog(LOGE, FL("chanwidth: %d"), chan_width); |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hddLog(LOGE, FL("hdd adapter cookie is invalid")); |
| return -EINVAL; |
| } |
| |
| if (WLAN_HDD_INFRA_STATION != adapter->device_mode) { |
| hddLog(LOGE, FL("Only Sta Mode supported!")); |
| return -ENOTSUPP; |
| } |
| |
| hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter); |
| if (hal_ptr == NULL) { |
| hddLog(LOGE, FL("hdd hal is null")); |
| return -EINVAL; |
| } |
| |
| sme_set_sta_chanlist_with_sub20(hal_ptr, chan_width); |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * hdd_get_sub20_channelwidth() - |
| * This api function get sub20 channel width |
| * @adapter: HDD adapter |
| * @sub20_channelwidth: restore sub20 channel width |
| * |
| * Return: The VOS_STATUS code associated with performing |
| * the operation |
| */ |
| static VOS_STATUS |
| hdd_get_sub20_channelwidth(hdd_adapter_t *adapter, uint32_t *sub20_channelwidth) |
| { |
| tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter); |
| tpAniSirGlobal mac_ptr = PMAC_STRUCT(hal_ptr); |
| v_CONTEXT_t vos_ctx_ptr = WLAN_HDD_GET_SAP_CTX_PTR(adapter); |
| uint32_t sap_sub20_channelwidth; |
| |
| if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) { |
| hddLog(LOGE, FL("hdd adapter cookie is invalid")); |
| return -EINVAL; |
| } |
| |
| if (WLAN_HDD_INFRA_STATION == adapter->device_mode) { |
| *sub20_channelwidth = mac_ptr->sta_sub20_current_channelwidth ? |
| mac_ptr->sta_sub20_current_channelwidth : |
| mac_ptr->sub20_channelwidth; |
| } else if (WLAN_HDD_SOFTAP == adapter->device_mode) { |
| WLANSAP_get_sub20_channelwidth(vos_ctx_ptr, |
| &sap_sub20_channelwidth); |
| *sub20_channelwidth = sap_sub20_channelwidth; |
| } else { |
| hddLog(LOGE, FL("error dev mode!")); |
| return -EINVAL; |
| } |
| |
| return VOS_STATUS_SUCCESS; |
| } |
| |
| /** |
| * __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) |
| { |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; |
| int ret_val = 0; |
| u32 modulated_dtim; |
| uint16_t stats_avg_factor, tx_rate; |
| uint8_t set_value, retry, qpower, delay; |
| uint32_t abs_delay; |
| u32 guard_time; |
| u32 ftm_capab; |
| eHalStatus status; |
| struct sir_set_tx_rx_aggregation_size request; |
| struct sir_set_rx_reorder_timeout_val reorder_timeout; |
| struct sir_peer_set_rx_blocksize rx_blocksize; |
| VOS_STATUS vos_status; |
| uint32_t tx_fail_count; |
| uint32_t antdiv_ena, antdiv_chain; |
| uint32_t antdiv_selftest, antdiv_selftest_intvl; |
| int attr_len; |
| int access_policy = 0; |
| char vendor_ie[SIR_MAC_MAX_IE_LENGTH + 2]; |
| bool vendor_ie_present = false, access_policy_present = false; |
| uint32_t ant_div_usrcfg; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret_val = wlan_hdd_validate_context(pHddCtx); |
| if (ret_val) |
| return ret_val; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, |
| data, data_len, |
| wlan_hdd_wifi_config_policy)) { |
| hddLog(LOGE, FL("invalid attr")); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]) { |
| ftm_capab = nla_get_u32(tb[ |
| QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]); |
| pHddCtx->cfg_ini->fine_time_meas_cap = |
| pHddCtx->fine_time_meas_cap_target & ftm_capab; |
| sme_update_fine_time_measurement_capab(pHddCtx->hHal, |
| pHddCtx->cfg_ini->fine_time_meas_cap); |
| hddLog(LOG1, |
| "FTM capability: user value: 0x%x, target value: 0x%x, final value: 0x%x", |
| ftm_capab, pHddCtx->fine_time_meas_cap_target, |
| pHddCtx->cfg_ini->fine_time_meas_cap); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM]) { |
| modulated_dtim = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM]); |
| |
| status = sme_configure_modulated_dtim(pHddCtx->hHal, |
| pAdapter->sessionId, |
| modulated_dtim); |
| |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EPERM; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR]) { |
| stats_avg_factor = nla_get_u16( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR]); |
| status = sme_configure_stats_avg_factor(pHddCtx->hHal, |
| pAdapter->sessionId, |
| stats_avg_factor); |
| |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EPERM; |
| } |
| |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME]) { |
| guard_time = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME]); |
| status = sme_configure_guard_time(pHddCtx->hHal, |
| pAdapter->sessionId, |
| guard_time); |
| |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EPERM; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_RATE]) { |
| tx_rate = nla_get_u16( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_RATE]); |
| hddLog(LOG1, "Config Txrate: tx_rate received :%d", tx_rate); |
| status = wlan_hdd_update_tx_rate(pHddCtx, tx_rate); |
| |
| if (eHAL_STATUS_SUCCESS != status) |
| ret_val = -EPERM; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND]) { |
| set_value = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND]); |
| hddLog(LOG1, "set_value: %d", set_value); |
| ret_val = hdd_enable_disable_ca_event(pHddCtx, set_value); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]) { |
| qpower = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]); |
| if (hdd_set_qpower_config(pHddCtx, pAdapter, qpower) != 0) |
| ret_val = -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]) { |
| /* if one is specified, both must be specified */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]) { |
| hddLog(LOGE, |
| FL("Both TX and RX MPDU Aggregation required")); |
| return -EINVAL; |
| } |
| |
| request.tx_aggregation_size = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION]); |
| request.rx_aggregation_size = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]); |
| request.vdev_id = pAdapter->sessionId; |
| |
| if (request.tx_aggregation_size >= |
| CFG_TX_AGGREGATION_SIZE_MIN && |
| request.tx_aggregation_size <= |
| CFG_TX_AGGREGATION_SIZE_MAX && |
| request.rx_aggregation_size >= |
| CFG_RX_AGGREGATION_SIZE_MIN && |
| request.rx_aggregation_size <= |
| CFG_RX_AGGREGATION_SIZE_MAX) { |
| vos_status = wma_set_tx_rx_aggregation_size(&request); |
| if (vos_status != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, |
| FL("failed to set aggr sizes err %d"), |
| vos_status); |
| ret_val = -EPERM; |
| } |
| } else { |
| hddLog(LOGE, |
| FL("TX %d RX %d MPDU aggr size not in range"), |
| request.tx_aggregation_size, |
| request.rx_aggregation_size); |
| ret_val = -EINVAL; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY]) { |
| retry = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY]); |
| |
| retry = retry > CFG_NON_AGG_RETRY_MAX ? |
| CFG_NON_AGG_RETRY_MAX : retry; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX]) { |
| status = sme_update_short_retry_limit_threshold( |
| pHddCtx->hHal, |
| pAdapter->sessionId, |
| retry); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_update_short_retry_limit_threshold(err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| } else { |
| ret_val = process_wma_set_command( |
| (int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, |
| retry, PDEV_CMD); |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY]) { |
| retry = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY]); |
| |
| retry = retry > CFG_AGG_RETRY_MAX ? |
| CFG_AGG_RETRY_MAX : retry; |
| |
| /* Value less than CFG_AGG_RETRY_MIN has side effect to t-put */ |
| retry = ((retry > 0) && (retry < CFG_AGG_RETRY_MIN)) ? |
| CFG_AGG_RETRY_MIN : retry; |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX]) { |
| status = sme_update_long_retry_limit_threshold( |
| pHddCtx->hHal, |
| pAdapter->sessionId, |
| retry); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_update_long_retry_limit_threshold(err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| } else { |
| ret_val = process_wma_set_command( |
| (int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_AGG_SW_RETRY_TH, |
| retry, PDEV_CMD); |
| } |
| |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY]) { |
| retry = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY]); |
| |
| retry = retry > CFG_MGMT_RETRY_MAX ? |
| CFG_MGMT_RETRY_MAX : retry; |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_MGMT_RETRY_LIMIT, |
| retry, PDEV_CMD); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY]) { |
| retry = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY]); |
| retry = retry > CFG_CTRL_RETRY_MAX ? |
| CFG_CTRL_RETRY_MAX : retry; |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_CTRL_RETRY_LIMIT, |
| retry, PDEV_CMD); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY]) { |
| delay = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY]); |
| delay = delay > CFG_PROPAGATION_DELAY_MAX ? |
| CFG_PROPAGATION_DELAY_MAX : delay; |
| abs_delay = delay + CFG_PROPAGATION_DELAY_BASE; |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_PROPAGATION_DELAY, |
| abs_delay, PDEV_CMD); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY]) { |
| abs_delay = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY]); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_PROPAGATION_DELAY, |
| abs_delay, PDEV_CMD); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT]) { |
| tx_fail_count = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT]); |
| if (tx_fail_count) { |
| status = sme_update_tx_fail_cnt_threshold(pHddCtx->hHal, |
| pAdapter->sessionId, |
| tx_fail_count); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_update_tx_fail_cnt_threshold (err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]) { |
| vos_mem_zero(&vendor_ie[0], SIR_MAC_MAX_IE_LENGTH + 2); |
| attr_len = nla_len( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]); |
| if (attr_len < 0 || attr_len > SIR_MAC_MAX_IE_LENGTH + 2) { |
| hddLog(LOGE, FL("Invalid value. attr_len %d"), |
| attr_len); |
| return -EINVAL; |
| } |
| |
| nla_memcpy(&vendor_ie, |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST], |
| attr_len); |
| vendor_ie_present = true; |
| hddLog(LOG1, FL("Access policy vendor ie present.attr_len %d"), |
| attr_len); |
| vos_trace_hex_dump(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| &vendor_ie[0], attr_len); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]) { |
| access_policy = (int) nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]); |
| if ((access_policy < QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED) || |
| (access_policy > QCA_ACCESS_POLICY_DENY_UNLESS_LISTED)){ |
| hddLog(LOGE, FL("Invalid value. access_policy %d"), |
| access_policy); |
| return -EINVAL; |
| } |
| access_policy_present = true; |
| hddLog(LOG1, FL("Access policy present. access_policy %d"), |
| access_policy); |
| } |
| |
| if (vendor_ie_present && access_policy_present) { |
| if (access_policy == QCA_ACCESS_POLICY_DENY_UNLESS_LISTED) { |
| access_policy = |
| WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED; |
| } |
| else { |
| access_policy = WLAN_HDD_VENDOR_IE_ACCESS_NONE; |
| } |
| |
| hddLog(LOG1, FL("calling sme_update_access_policy_vendor_ie")); |
| status = sme_update_access_policy_vendor_ie(pHddCtx->hHal, |
| pAdapter->sessionId, &vendor_ie[0], |
| access_policy); |
| if (status == eHAL_STATUS_FAILURE) { |
| hddLog(LOGE, FL( |
| "Failed to set vendor ie and access policy.")); |
| return -EINVAL; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]) { |
| antdiv_ena = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]); |
| hddLog(LOG1, FL("antdiv_ena: %d"), antdiv_ena); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ENA_ANT_DIV, |
| antdiv_ena, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set antdiv_ena")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]) { |
| antdiv_chain = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]); |
| hddLog(LOG1, FL("antdiv_chain: %d"), antdiv_chain); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_FORCE_CHAIN_ANT, |
| antdiv_chain, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set antdiv_chain")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]) { |
| antdiv_selftest = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]); |
| hddLog(LOG1, FL("antdiv_selftest: %d"), antdiv_selftest); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST, |
| antdiv_selftest, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set antdiv_selftest")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]) { |
| antdiv_selftest_intvl = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]); |
| hddLog(LOG1, FL("antdiv_selftest_intvl: %d"), |
| antdiv_selftest_intvl); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL, |
| antdiv_selftest_intvl, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set antdiv_selftest_intvl")); |
| return ret_val; |
| } |
| } |
| |
| #define RX_TIMEOUT_VAL_MIN 10 |
| #define RX_TIMEOUT_VAL_MAX 1000 |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND]) { |
| |
| /* if one is specified, all must be specified */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND]) { |
| hddLog(LOGE, |
| FL("four AC timeout val are required MAC")); |
| return -EINVAL; |
| } |
| |
| reorder_timeout.rx_timeout_pri[0] = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE]); |
| reorder_timeout.rx_timeout_pri[1] = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO]); |
| reorder_timeout.rx_timeout_pri[2] = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT]); |
| reorder_timeout.rx_timeout_pri[3] = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_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) { |
| vos_status = sme_set_reorder_timeout(pHddCtx->hHal, |
| &reorder_timeout); |
| if (vos_status != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, |
| FL("failed to set reorder timeout err %d"), |
| vos_status); |
| ret_val = -EPERM; |
| } |
| } else { |
| hddLog(LOGE, |
| FL("one of the timeout value is not in range")); |
| ret_val = -EINVAL; |
| } |
| } |
| |
| #define WINDOW_SIZE_VAL_MIN 1 |
| #define WINDOW_SIZE_VAL_MAX 64 |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT]) { |
| |
| /* if one is specified, both must be specified */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT]) { |
| hddLog(LOGE, |
| FL("Both Peer MAC and windows limit required")); |
| return -EINVAL; |
| } |
| |
| memcpy(&rx_blocksize.peer_macaddr, |
| nla_data(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC]), |
| sizeof(rx_blocksize.peer_macaddr)), |
| |
| rx_blocksize.vdev_id = pAdapter->sessionId; |
| set_value = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_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; |
| vos_status = sme_set_rx_set_blocksize(pHddCtx->hHal, |
| &rx_blocksize); |
| if (vos_status != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, |
| FL("failed to set aggr sizes err %d"), |
| vos_status); |
| ret_val = -EPERM; |
| } |
| } else { |
| hddLog(LOGE, FL("window size val is not in range")); |
| ret_val = -EINVAL; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) { |
| bool manual_set_sub20; |
| uint32_t sub20_chan_width; |
| |
| sub20_chan_width = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]); |
| hddLog(LOGE, FL("SUB20 %d"), sub20_chan_width); |
| |
| switch (sub20_chan_width) { |
| case NL80211_CHAN_WIDTH_5: |
| sub20_chan_width = SUB20_MODE_5MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_10: |
| sub20_chan_width = SUB20_MODE_10MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_20_NOHT: |
| sub20_chan_width = SUB20_MODE_NONE; |
| break; |
| default: |
| hddLog(LOGE, FL("invalid param %d"), sub20_chan_width); |
| return -EINVAL; |
| } |
| manual_set_sub20 = |
| hdd_sub20_channelwidth_can_set(pAdapter, |
| sub20_chan_width); |
| if (!manual_set_sub20) { |
| hddLog(LOGE, FL("STA can't set sub20 chanwidth")); |
| return -EINVAL; |
| } |
| hdd_sta_set_sub20_channelwidth(pAdapter, sub20_chan_width); |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]) { |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]) { |
| hddLog(LOGE, FL("Both probe and stay period required")); |
| return -EINVAL; |
| } |
| |
| ant_div_usrcfg = ANT_DIV_SET_PERIOD( |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD]), |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD])); |
| hddLog(LOG1, FL("ant div set period: %x"), ant_div_usrcfg); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set ant div period")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF]) { |
| ant_div_usrcfg = ANT_DIV_SET_SNR_DIFF( |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF])); |
| hddLog(LOG1, FL("ant div set snr diff: %x"), ant_div_usrcfg); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set ant snr diff")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME]) { |
| ant_div_usrcfg = ANT_DIV_SET_PROBE_DWELL_TIME( |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME])); |
| hddLog(LOG1, FL("ant div set probe dewll time: %x"), ant_div_usrcfg); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set ant div probe dewll time")); |
| return ret_val; |
| } |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] || |
| tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]) { |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] || |
| !tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]) { |
| hddLog(LOGE, FL("Mgmt snr, data snr and ack snr weight are required")); |
| return -EINVAL; |
| } |
| |
| ant_div_usrcfg = ANT_DIV_SET_WEIGHT( |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT]), |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT]), |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT])); |
| hddLog(LOG1, FL("ant div set weight: %x"), ant_div_usrcfg); |
| ret_val = process_wma_set_command((int)pAdapter->sessionId, |
| (int)WMI_PDEV_PARAM_ANT_DIV_USRCFG, |
| ant_div_usrcfg, PDEV_CMD); |
| if (ret_val) { |
| hddLog(LOG1, FL("Failed to set ant div weight")); |
| return ret_val; |
| } |
| } |
| |
| return ret_val; |
| } |
| |
| /** |
| * 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __wlan_hdd_cfg80211_wifi_configuration_get() - |
| * Get the wifi configuration 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 |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| __wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; |
| uint32_t sub20_chan_width = 0; |
| int status; |
| struct sk_buff *reply_skb; |
| uint32_t skb_len = 0, count = 0; |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, |
| data_len, wlan_hdd_wifi_config_policy)) { |
| hddLog(LOGE, FL("WIFI_CFG_GET NL CMD parsing failed")); |
| return -EINVAL; |
| } |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) { |
| hdd_get_sub20_channelwidth(adapter, &sub20_chan_width); |
| |
| switch (sub20_chan_width) { |
| case SUB20_MODE_5MHZ: |
| sub20_chan_width = NL80211_CHAN_WIDTH_5; |
| break; |
| case SUB20_MODE_10MHZ: |
| sub20_chan_width = NL80211_CHAN_WIDTH_10; |
| break; |
| case SUB20_MODE_NONE: |
| sub20_chan_width = NL80211_CHAN_WIDTH_20_NOHT; |
| break; |
| default: |
| hddLog(LOGE, FL("invalid val %d"), sub20_chan_width); |
| return -EINVAL; |
| } |
| hddLog(LOGE, FL("SUB20 chanwidth %d"), sub20_chan_width); |
| |
| skb_len += sizeof(sub20_chan_width); |
| count++; |
| } |
| |
| if (count == 0) { |
| hddLog(LOGE, FL("unknown attribute in get_wifi_cfg request")); |
| return -EINVAL; |
| } |
| |
| skb_len += NLMSG_HDRLEN; |
| reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len); |
| |
| if (!reply_skb) |
| goto error_skb_fail; |
| |
| if (tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) { |
| if (nla_put_u32(reply_skb, |
| QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH, |
| sub20_chan_width)) |
| goto error_nla_fail; |
| } |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| |
| error_skb_fail: |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| |
| error_nla_fail: |
| hddLog(LOGE, FL("nla put fail")); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_wifi_configuration_get() - Get the wifi configuration 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 |
| * |
| * Return: Return the Success or Failure code. |
| */ |
| static int |
| wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_wifi_configuration_get(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #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 Capabilites. |
| * @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; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct sk_buff *skb; |
| uint32_t set = 0; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| 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) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| goto fail; |
| } |
| |
| if (FALSE == hdd_ctx->cfg_ini->fEnableTDLSSupport) { |
| hddLog(LOGE, |
| FL("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)) { |
| hddLog(LOGE, FL("nla put fail")); |
| goto fail; |
| } |
| } else { |
| set = set | WIFI_TDLS_SUPPORT; |
| set = set | (hdd_ctx->cfg_ini->fTDLSExternalControl ? |
| WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT : 0); |
| set = set | (hdd_ctx->cfg_ini->fEnableTDLSOffChannel ? |
| WIIF_TDLS_OFFCHANNEL_SUPPORT : 0); |
| hddLog(LOG1, FL("TDLS Feature supported value %x"), set); |
| if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, |
| hdd_ctx->max_num_tdls_sta) || |
| nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, |
| set)) { |
| hddLog(LOGE, FL("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 Capabilites. |
| * @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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_tdls_capabilities(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif |
| |
| static 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 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) |
| { |
| eHalStatus status; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1]; |
| struct sir_wifi_start_log start_log; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX, |
| data, data_len, |
| qca_wlan_vendor_wifi_logger_start_policy)) { |
| hddLog(LOGE, FL("Invalid attribute")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ring id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) { |
| hddLog(LOGE, FL("attr ATTR failed")); |
| return -EINVAL; |
| } |
| start_log.ring_id = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]); |
| hddLog(LOG1, FL("Ring ID=%d"), start_log.ring_id); |
| |
| /* Parse and fetch verbose level */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) { |
| hddLog(LOGE, FL("attr verbose_level failed")); |
| return -EINVAL; |
| } |
| start_log.verbose_level = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]); |
| hddLog(LOG1, FL("verbose_level=%d"), start_log.verbose_level); |
| |
| /* Parse and fetch flag */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) { |
| hddLog(LOGE, FL("attr flag failed")); |
| return -EINVAL; |
| } |
| start_log.flag = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]); |
| hddLog(LOG1, FL("flag=%d"), start_log.flag); |
| |
| vos_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) |
| vos_set_wakelock_logging(true); |
| else |
| vos_set_wakelock_logging(false); |
| return 0; |
| } |
| |
| status = sme_wifi_start_logger(hdd_ctx->hHal, start_log); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_wifi_start_logger failed(err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static const struct |
| nla_policy |
| qca_wlan_vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_MAX+1] = { |
| [QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = { |
| .type = NLA_BINARY, |
| .len = HDD_MAC_ADDR_LEN}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_get_link_properties() - This function is used to |
| * get link properties like nss, rate flags and operating frequency for |
| * the connection with the given peer. |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function return the above link properties on success. |
| * |
| * 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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_station_ctx_t *hdd_sta_ctx; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1]; |
| uint8_t peer_mac[VOS_MAC_ADDR_SIZE]; |
| uint32_t sta_id; |
| struct sk_buff *reply_skb; |
| uint32_t rate_flags = 0; |
| uint8_t nss; |
| uint8_t final_rate_flags = 0; |
| uint32_t freq; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (0 != wlan_hdd_validate_context(hdd_ctx)) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, |
| qca_wlan_vendor_attr_policy)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid attribute")); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Attribute peerMac not provided for mode=%d"), |
| adapter->device_mode); |
| return -EINVAL; |
| } |
| |
| if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < sizeof(peer_mac)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Attribute peerMac is invalid=%d"), |
| adapter->device_mode); |
| return -EINVAL; |
| } |
| |
| memcpy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), |
| sizeof(peer_mac)); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("peerMac="MAC_ADDRESS_STR" for device_mode:%d"), |
| MAC_ADDR_ARRAY(peer_mac), adapter->device_mode); |
| |
| if (adapter->device_mode == WLAN_HDD_INFRA_STATION || |
| adapter->device_mode == WLAN_HDD_P2P_CLIENT) { |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if ((hdd_sta_ctx->conn_info.connState != |
| eConnectionState_Associated) || |
| !vos_mem_compare(hdd_sta_ctx->conn_info.bssId, peer_mac, |
| VOS_MAC_ADDRESS_LEN)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Not Associated to mac "MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(peer_mac)); |
| return -EINVAL; |
| } |
| |
| nss = hdd_sta_ctx->conn_info.nss; |
| freq = vos_chan_to_freq( |
| hdd_sta_ctx->conn_info.operationChannel); |
| rate_flags = hdd_sta_ctx->conn_info.rate_flags; |
| } else if (adapter->device_mode == WLAN_HDD_P2P_GO || |
| adapter->device_mode == WLAN_HDD_SOFTAP) { |
| |
| for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) { |
| if (adapter->aStaInfo[sta_id].isUsed && |
| !vos_is_macaddr_broadcast( |
| &adapter->aStaInfo[sta_id].macAddrSTA) && |
| vos_mem_compare( |
| &adapter->aStaInfo[sta_id].macAddrSTA, |
| peer_mac, VOS_MAC_ADDRESS_LEN)) |
| break; |
| } |
| |
| if (WLAN_MAX_STA_COUNT == sta_id) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("No active peer with mac="MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(peer_mac)); |
| return -EINVAL; |
| } |
| |
| nss = adapter->aStaInfo[sta_id].nss; |
| freq = vos_chan_to_freq( |
| (WLAN_HDD_GET_AP_CTX_PTR(adapter))->operatingChannel); |
| rate_flags = adapter->aStaInfo[sta_id].rate_flags; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Not Associated! with mac"MAC_ADDRESS_STR), |
| MAC_ADDR_ARRAY(peer_mac)); |
| return -EINVAL; |
| } |
| |
| if (!(rate_flags & eHAL_TX_RATE_LEGACY)) { |
| if (rate_flags & eHAL_TX_RATE_VHT80) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) |
| final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; |
| #endif |
| } else if (rate_flags & eHAL_TX_RATE_VHT40) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) |
| final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; |
| #endif |
| } else if (rate_flags & eHAL_TX_RATE_VHT20) { |
| final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS; |
| } else if (rate_flags & (eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40)) { |
| final_rate_flags |= RATE_INFO_FLAGS_MCS; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) |
| if (rate_flags & eHAL_TX_RATE_HT40) |
| final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; |
| #endif |
| } |
| |
| if (rate_flags & eHAL_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 (NULL == reply_skb) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("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)) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_put failed")); |
| kfree_skb(reply_skb); |
| return -EINVAL; |
| } |
| |
| return cfg80211_vendor_cmd_reply(reply_skb); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_link_properties() - This function is used to |
| * get link properties like nss, rate flags and operating frequency for |
| * the connection with the given peer. |
| * @wiphy: WIPHY structure pointer |
| * @wdev: Wireless device structure pointer |
| * @data: Pointer to the data received |
| * @data_len: Length of the data received |
| * |
| * This function return the above link properties on success. |
| * |
| * 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_link_properties(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * 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) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_wifi_logger_start(wiphy, |
| wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static 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) |
| { |
| eHalStatus status; |
| VOS_STATUS ret; |
| uint32_t ring_id; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb |
| [QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1]; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| if (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)) { |
| hddLog(LOGE, FL("Invalid attribute")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch ring id */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) { |
| hddLog(LOGE, FL("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(); |
| hddLog(LOG1, FL("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 |
| */ |
| hddLog(LOG1, FL("Bug report triggered by framework")); |
| |
| ret = vos_flush_logs(WLAN_LOG_TYPE_NON_FATAL, |
| WLAN_LOG_INDICATOR_FRAMEWORK, |
| WLAN_LOG_REASON_CODE_UNUSED, |
| DUMP_VOS_TRACE | DUMP_PACKET_TRACE); |
| if (VOS_STATUS_SUCCESS != ret) { |
| hddLog(LOGE, FL("Failed to trigger bug report")); |
| return -EINVAL; |
| } |
| } else |
| wlan_report_log_completion(FALSE, WLAN_LOG_INDICATOR_FRAMEWORK, |
| WLAN_LOG_REASON_CODE_UNUSED); |
| 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) |
| { |
| int ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy, |
| wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #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(hdd_context_t *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 -EINVAL; |
| } |
| |
| /** |
| * 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(hdd_context_t *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 |
| |
| /** |
| * 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(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx, |
| struct nlattr **tb) |
| { |
| struct sSirAddPeriodicTxPtrn *add_req; |
| eHalStatus status; |
| uint32_t request_id, ret, len; |
| uint8_t pattern_id = 0; |
| v_MACADDR_t dst_addr; |
| uint16_t eth_type = htons(ETH_P_IP); |
| |
| if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { |
| hddLog(LOGE, FL("Not in Connected state!")); |
| return -ENOTSUPP; |
| } |
| |
| add_req = vos_mem_malloc(sizeof(*add_req)); |
| if (!add_req) { |
| hddLog(LOGE, FL("memory allocation failed")); |
| return -ENOMEM; |
| } |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| goto fail; |
| } |
| |
| request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| if (request_id == MAX_REQUEST_ID) { |
| hddLog(LOGE, FL("request_id cannot be MAX")); |
| goto fail; |
| } |
| |
| hddLog(LOG1, FL("Request Id: %u"), request_id); |
| |
| if (!tb[PARAM_PERIOD]) { |
| hddLog(LOGE, FL("attr period failed")); |
| goto fail; |
| } |
| add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]); |
| hddLog(LOG1, FL("Period: %u ms"), add_req->usPtrnIntervalMs); |
| if (add_req->usPtrnIntervalMs == 0) { |
| hddLog(LOGE, FL("Invalid interval zero, return failure")); |
| goto fail; |
| } |
| |
| if (!tb[PARAM_SRC_MAC_ADDR]) { |
| hddLog(LOGE, FL("attr source mac address failed")); |
| goto fail; |
| } |
| nla_memcpy(add_req->macAddress, tb[PARAM_SRC_MAC_ADDR], |
| VOS_MAC_ADDR_SIZE); |
| hddLog(LOG1, "input src mac address: "MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(add_req->macAddress)); |
| |
| if (memcmp(add_req->macAddress, adapter->macAddressCurrent.bytes, |
| VOS_MAC_ADDR_SIZE)) { |
| hddLog(LOGE, FL("input src mac address and connected ap bssid are different")); |
| goto fail; |
| } |
| |
| if (!tb[PARAM_DST_MAC_ADDR]) { |
| hddLog(LOGE, FL("attr dst mac address failed")); |
| goto fail; |
| } |
| nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], VOS_MAC_ADDR_SIZE); |
| hddLog(LOG1, "input dst mac address: "MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(dst_addr.bytes)); |
| |
| if (!tb[PARAM_IP_PACKET]) { |
| hddLog(LOGE, FL("attr ip packet failed")); |
| goto fail; |
| } |
| add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]); |
| hddLog(LOG1, FL("IP packet len: %u"), add_req->ucPtrnSize); |
| |
| if (add_req->ucPtrnSize < 0 || |
| add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE - |
| HDD_ETH_HEADER_LEN)) { |
| hddLog(LOGE, FL("Invalid IP packet len: %d"), |
| add_req->ucPtrnSize); |
| goto fail; |
| } |
| |
| len = 0; |
| vos_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, VOS_MAC_ADDR_SIZE); |
| len += VOS_MAC_ADDR_SIZE; |
| vos_mem_copy(&add_req->ucPattern[len], add_req->macAddress, |
| VOS_MAC_ADDR_SIZE); |
| len += VOS_MAC_ADDR_SIZE; |
| vos_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 | |
| * ------------------------------------------------------------ |
| */ |
| vos_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) { |
| hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret); |
| goto fail; |
| } |
| add_req->ucPtrnId = pattern_id; |
| hddLog(LOG1, FL("pattern id: %d"), add_req->ucPtrnId); |
| |
| status = sme_AddPeriodicTxPtrn(hdd_ctx->hHal, add_req); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_AddPeriodicTxPtrn failed (err=%d)"), status); |
| goto fail; |
| } |
| |
| EXIT(); |
| vos_mem_free(add_req); |
| return 0; |
| |
| fail: |
| vos_mem_free(add_req); |
| return -EINVAL; |
| } |
| |
| /** |
| * 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(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx, |
| struct nlattr **tb) |
| { |
| struct sSirDelPeriodicTxPtrn *del_req; |
| eHalStatus status; |
| uint32_t request_id, ret; |
| uint8_t pattern_id = 0; |
| |
| /* Parse and fetch request Id */ |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| return -EINVAL; |
| } |
| |
| request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| if (request_id == MAX_REQUEST_ID) { |
| hddLog(LOGE, FL("request_id cannot be MAX")); |
| return -EINVAL; |
| } |
| |
| ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id); |
| if (ret) { |
| hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret); |
| return -EINVAL; |
| } |
| |
| del_req = vos_mem_malloc(sizeof(*del_req)); |
| if (!del_req) { |
| hddLog(LOGE, FL("memory allocation failed")); |
| return -ENOMEM; |
| } |
| |
| vos_mem_copy(del_req->macAddress, adapter->macAddressCurrent.bytes, |
| VOS_MAC_ADDR_SIZE); |
| hddLog(LOG1, MAC_ADDRESS_STR, MAC_ADDR_ARRAY(del_req->macAddress)); |
| del_req->ucPtrnId = pattern_id; |
| hddLog(LOG1, FL("Request Id: %u Pattern id: %d"), |
| request_id, del_req->ucPtrnId); |
| |
| status = sme_DelPeriodicTxPtrn(hdd_ctx->hHal, del_req); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_DelPeriodicTxPtrn failed (err=%d)"), status); |
| goto fail; |
| } |
| |
| EXIT(); |
| vos_mem_free(del_req); |
| return 0; |
| |
| fail: |
| vos_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; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| uint8_t control; |
| int ret; |
| static const struct nla_policy policy[PARAM_MAX + 1] = { |
| [PARAM_REQUEST_ID] = { .type = NLA_U32 }, |
| [PARAM_CONTROL] = { .type = NLA_U32 }, |
| [PARAM_SRC_MAC_ADDR] = { .type = NLA_BINARY, |
| .len = VOS_MAC_ADDR_SIZE }, |
| [PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY, |
| .len = VOS_MAC_ADDR_SIZE }, |
| [PARAM_PERIOD] = { .type = NLA_U32 }, |
| }; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (!sme_IsFeatureSupportedByFW(WLAN_PERIODIC_TX_PTRN)) { |
| hddLog(LOGE, |
| FL("Periodic Tx Pattern Offload feature is not supported in FW!")); |
| return -ENOTSUPP; |
| } |
| |
| if (nla_parse(tb, PARAM_MAX, data, data_len, policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| if (!tb[PARAM_CONTROL]) { |
| hddLog(LOGE, FL("attr control failed")); |
| return -EINVAL; |
| } |
| control = nla_get_u32(tb[PARAM_CONTROL]); |
| hddLog(LOG1, FL("Control: %d"), control); |
| |
| if (control == WLAN_START_OFFLOADED_PACKETS) |
| return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb); |
| else if (control == WLAN_STOP_OFFLOADED_PACKETS) |
| return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb); |
| else { |
| hddLog(LOGE, FL("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 |
| |
| /** |
| * 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 ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_offloaded_packets(wiphy, |
| wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_monitor_rssi() |
| */ |
| #define PARAM_MAX QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX |
| #define PARAM_REQUEST_ID QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID |
| #define PARAM_CONTROL QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL |
| #define PARAM_MIN_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI |
| #define PARAM_MAX_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI |
| |
| /** |
| * __wlan_hdd_cfg80211_monitor_rssi() - monitor rssi |
| * @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_monitor_rssi(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[PARAM_MAX + 1]; |
| struct rssi_monitor_req req; |
| eHalStatus status; |
| int ret; |
| uint32_t control; |
| static const struct nla_policy policy[PARAM_MAX + 1] = { |
| [PARAM_REQUEST_ID] = { .type = NLA_U32 }, |
| [PARAM_CONTROL] = { .type = NLA_U32 }, |
| [PARAM_MIN_RSSI] = { .type = NLA_S8 }, |
| [PARAM_MAX_RSSI] = { .type = NLA_S8 }, |
| }; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { |
| hddLog(LOGE, FL("Not in Connected state!")); |
| return -ENOTSUPP; |
| } |
| |
| if (nla_parse(tb, PARAM_MAX, data, data_len, policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| if (!tb[PARAM_REQUEST_ID]) { |
| hddLog(LOGE, FL("attr request id failed")); |
| return -EINVAL; |
| } |
| |
| if (!tb[PARAM_CONTROL]) { |
| hddLog(LOGE, FL("attr control failed")); |
| return -EINVAL; |
| } |
| |
| req.request_id = nla_get_u32(tb[PARAM_REQUEST_ID]); |
| req.session_id = adapter->sessionId; |
| control = nla_get_u32(tb[PARAM_CONTROL]); |
| |
| if (control == QCA_WLAN_RSSI_MONITORING_START) { |
| req.control = true; |
| if (!tb[PARAM_MIN_RSSI]) { |
| hddLog(LOGE, FL("attr min rssi failed")); |
| return -EINVAL; |
| } |
| |
| if (!tb[PARAM_MAX_RSSI]) { |
| hddLog(LOGE, FL("attr max rssi failed")); |
| return -EINVAL; |
| } |
| |
| req.min_rssi = nla_get_s8(tb[PARAM_MIN_RSSI]); |
| req.max_rssi = nla_get_s8(tb[PARAM_MAX_RSSI]); |
| |
| if (!(req.min_rssi < req.max_rssi)) { |
| hddLog(LOGW, FL("min_rssi: %d must be less than max_rssi: %d"), |
| req.min_rssi, req.max_rssi); |
| return -EINVAL; |
| } |
| hddLog(LOG1, FL("Min_rssi: %d Max_rssi: %d"), |
| req.min_rssi, req.max_rssi); |
| |
| } else if (control == QCA_WLAN_RSSI_MONITORING_STOP) |
| req.control = false; |
| else { |
| hddLog(LOGE, FL("Invalid control cmd: %d"), control); |
| return -EINVAL; |
| } |
| hddLog(LOG1, FL("Request Id: %u Session_id: %d Control: %d"), |
| req.request_id, req.session_id, req.control); |
| |
| status = sme_set_rssi_monitoring(hdd_ctx->hHal, &req); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_set_rssi_monitoring failed(err=%d)"), status); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * done with short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_monitor_rssi() |
| */ |
| #undef PARAM_MAX |
| #undef PARAM_CONTROL |
| #undef PARAM_REQUEST_ID |
| #undef PARAM_MAX_RSSI |
| #undef PARAM_MIN_RSSI |
| |
| /** |
| * wlan_hdd_cfg80211_monitor_rssi() - SSR wrapper to rssi monitoring |
| * @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_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_monitor_rssi(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_rssi_threshold_breached() - rssi breached NL event |
| * @hddctx: HDD context |
| * @data: rssi breached event data |
| * |
| * This function reads the rssi breached event %data and fill in the skb with |
| * NL attributes and send up the NL event. |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| void hdd_rssi_threshold_breached(void *hddctx, |
| struct rssi_breach_event *data) |
| { |
| hdd_context_t *hdd_ctx = hddctx; |
| struct sk_buff *skb; |
| int flags = vos_get_gfp_flags(); |
| |
| ENTER(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!data) { |
| hddLog(LOGE, FL("data is null")); |
| return; |
| } |
| |
| skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, |
| NULL, |
| EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN, |
| QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX, |
| flags); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); |
| return; |
| } |
| |
| hddLog(LOG1, "Req Id: %u Current rssi: %d", |
| data->request_id, data->curr_rssi); |
| hddLog(LOG1, "Current BSSID: "MAC_ADDRESS_STR, |
| MAC_ADDR_ARRAY(data->curr_bssid.bytes)); |
| |
| if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, |
| data->request_id) || |
| nla_put(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, |
| sizeof(data->curr_bssid), data->curr_bssid.bytes) || |
| nla_put_s8(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, |
| data->curr_rssi)) { |
| hddLog(LOGE, FL("nla put fail")); |
| goto fail; |
| } |
| |
| cfg80211_vendor_event(skb, flags); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| return; |
| } |
| #define PWR_SAVE_FAIL_CMD_INDEX \ |
| QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX |
| /** |
| * hdd_chip_pwr_save_fail_detected() - chip power save failure detected |
| * NL event |
| * @hddctx: HDD context |
| * @data: chip power save failure detected data |
| * |
| * This function reads the chip power save failure detected data and fill in |
| * the skb with NL attributes and send up the NL event. |
| * This callback execute in atomic context and must not invoke any |
| * blocking calls. |
| * |
| * Return: none |
| */ |
| void hdd_chip_pwr_save_fail_detected_cb(void *hddctx, |
| struct chip_pwr_save_fail_detected_params |
| *data) |
| { |
| hdd_context_t *hdd_ctx = hddctx; |
| struct sk_buff *skb; |
| int flags = vos_get_gfp_flags(); |
| |
| ENTER(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!data) { |
| hddLog(LOGE, FL("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) { |
| hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); |
| return; |
| } |
| |
| hddLog(LOG1, "failure reason code: %u, wake lock bitmap : %u %u %u %u", |
| data->failure_reason_code, |
| data->wake_lock_bitmap[0], |
| data->wake_lock_bitmap[1], |
| data->wake_lock_bitmap[2], |
| data->wake_lock_bitmap[3]); |
| |
| if (nla_put_u32(skb, |
| QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON, |
| data->failure_reason_code)) |
| goto fail; |
| |
| cfg80211_vendor_event(skb, flags); |
| EXIT(); |
| return; |
| |
| fail: |
| kfree_skb(skb); |
| EXIT(); |
| return; |
| } |
| #undef PWR_SAVE_FAIL_CMD_INDEX |
| |
| static 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]; |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| ENTER(); |
| |
| status = wlan_hdd_validate_context(pHddCtx); |
| if (0 != status) |
| return status; |
| if (TRUE != pHddCtx->cfg_ini->fhostNSOffload) { |
| hddLog(LOGE, FL("ND Offload not supported")); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX, |
| (struct nlattr *)data, |
| data_len, ns_offload_set_policy)) { |
| hddLog(LOGE, FL("nla_parse failed")); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]) { |
| hddLog(LOGE, FL("ND Offload flag attribute not present")); |
| return -EINVAL; |
| } |
| |
| pHddCtx->ns_offload_enable = |
| nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]); |
| |
| /* |
| * If active mode offload is enabled configure the nsoffload |
| * enable/disable request from the upper layer. |
| */ |
| if (pHddCtx->cfg_ini->active_mode_offload) { |
| hddLog(LOG1, |
| FL("Configure NS offload with command: %d"), |
| pHddCtx->ns_offload_enable); |
| hdd_conf_ns_offload(pAdapter, pHddCtx->ns_offload_enable); |
| } |
| |
| 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_set_ns_offload(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * __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 net_device *dev = wdev->netdev; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| int ret; |
| static const struct nla_policy policy[QCA_WLAN_VENDOR_ATTR_MAX + 1] |
| = {[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = { .type = NLA_U32 } }; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) { |
| hddLog(LOGE, FL("attr SETBAND_VALUE failed")); |
| return -EINVAL; |
| } |
| |
| return hdd_setBand(dev, |
| nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE])); |
| } |
| |
| /** |
| * 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 ret = 0; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_setband(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_bpf_offload() |
| */ |
| #define BPF_INVALID \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID |
| #define BPF_SET_RESET \ |
| QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER |
| #define BPF_VERSION \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION |
| #define BPF_FILTER_ID \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID |
| #define BPF_PACKET_SIZE \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE |
| #define BPF_CURRENT_OFFSET \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET |
| #define BPF_PROGRAM \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM |
| #define BPF_MAX \ |
| QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX |
| |
| static const struct nla_policy |
| wlan_hdd_bpf_offload_policy[BPF_MAX + 1] = { |
| [BPF_SET_RESET] = {.type = NLA_U32}, |
| [BPF_VERSION] = {.type = NLA_U32}, |
| [BPF_FILTER_ID] = {.type = NLA_U32}, |
| [BPF_PACKET_SIZE] = {.type = NLA_U32}, |
| [BPF_CURRENT_OFFSET] = {.type = NLA_U32}, |
| [BPF_PROGRAM] = {.type = NLA_U8}, |
| }; |
| |
| /** |
| * hdd_get_bpf_offload_cb() - Callback function to BPF Offload |
| * @hdd_context: hdd_context |
| * @bpf_get_offload: struct for get offload |
| * |
| * 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 |
| */ |
| void hdd_get_bpf_offload_cb(void *hdd_context, |
| struct sir_bpf_get_offload *data) |
| { |
| hdd_context_t *hdd_ctx = hdd_context; |
| struct hdd_bpf_context *context; |
| |
| ENTER(); |
| |
| if (wlan_hdd_validate_context(hdd_ctx)) |
| return; |
| |
| if (!data) { |
| hddLog(LOGE, FL("Data is null")); |
| return; |
| } |
| spin_lock(&hdd_context_lock); |
| |
| context = &bpf_context; |
| /* The caller presumably timed out so there is nothing we can do */ |
| if (context->magic != BPF_CONTEXT_MAGIC) { |
| spin_unlock(&hdd_context_lock); |
| return; |
| } |
| |
| /* context is valid so caller is still waiting */ |
| /* paranoia: invalidate the magic */ |
| context->magic = 0; |
| |
| context->capability_response = *data; |
| complete(&context->completion); |
| |
| spin_unlock(&hdd_context_lock); |
| |
| return; |
| } |
| |
| /** |
| * hdd_post_get_bpf_capabilities_rsp() - Callback function to BPF Offload |
| * @hdd_context: hdd_context |
| * @bpf_get_offload: struct for get offload |
| * |
| * Return: 0 on success, error number otherwise. |
| */ |
| static int hdd_post_get_bpf_capabilities_rsp(hdd_context_t *hdd_ctx, |
| struct sir_bpf_get_offload *bpf_get_offload) |
| { |
| struct sk_buff *skb; |
| uint32_t nl_buf_len; |
| |
| ENTER(); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += |
| (sizeof(bpf_get_offload->max_bytes_for_bpf_inst) + NLA_HDRLEN) + |
| (sizeof(bpf_get_offload->bpf_version) + NLA_HDRLEN); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| hddLog(LOG1, "BPF Version: %u BPF max bytes: %u", |
| bpf_get_offload->bpf_version, |
| bpf_get_offload->max_bytes_for_bpf_inst); |
| |
| if (nla_put_u32(skb, BPF_PACKET_SIZE, |
| bpf_get_offload->max_bytes_for_bpf_inst) || |
| nla_put_u32(skb, BPF_VERSION, bpf_get_offload->bpf_version)) { |
| hddLog(LOGE, FL("nla put failure")); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| EXIT(); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_get_bpf_offload - Get BPF offload Capabilities |
| * @hdd_ctx: Hdd context |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int hdd_get_bpf_offload(hdd_context_t *hdd_ctx) |
| { |
| unsigned long rc; |
| struct hdd_bpf_context *context; |
| eHalStatus hstatus; |
| int ret; |
| |
| ENTER(); |
| |
| spin_lock(&hdd_context_lock); |
| context = &bpf_context; |
| context->magic = BPF_CONTEXT_MAGIC; |
| INIT_COMPLETION(context->completion); |
| spin_unlock(&hdd_context_lock); |
| |
| hstatus = sme_get_bpf_offload_capabilities(hdd_ctx->hHal); |
| if (!HAL_STATUS_SUCCESS(hstatus)) { |
| hddLog(LOGE, FL("Unable to retrieve BPF caps")); |
| return -EINVAL; |
| } |
| /* request was sent -- wait for the response */ |
| rc = wait_for_completion_timeout(&context->completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_BPF)); |
| if (!rc) { |
| hddLog(LOGE, FL("Target response timed out")); |
| spin_lock(&hdd_context_lock); |
| context->magic = 0; |
| spin_unlock(&hdd_context_lock); |
| |
| return -ETIMEDOUT; |
| } |
| ret = hdd_post_get_bpf_capabilities_rsp(hdd_ctx, |
| &bpf_context.capability_response); |
| if (ret) |
| hddLog(LOGE, FL("Failed to post get bpf capabilities")); |
| |
| EXIT(); |
| return ret; |
| } |
| |
| /** |
| * hdd_set_reset_bpf_offload - Post set/reset bpf to SME |
| * @hdd_ctx: Hdd context |
| * @tb: Length of @data |
| * @adapter: pointer to adapter struct |
| * |
| * Return: 0 on success; errno on failure |
| */ |
| static int hdd_set_reset_bpf_offload(hdd_context_t *hdd_ctx, |
| struct nlattr **tb, |
| hdd_adapter_t *adapter) |
| { |
| struct sir_bpf_set_offload *bpf_set_offload; |
| eHalStatus hstatus; |
| int prog_len; |
| int ret_val = -EINVAL; |
| |
| ENTER(); |
| |
| if (adapter->device_mode == WLAN_HDD_INFRA_STATION || |
| adapter->device_mode == WLAN_HDD_P2P_CLIENT) { |
| if (!hdd_connIsConnected( |
| WLAN_HDD_GET_STATION_CTX_PTR(adapter))) { |
| hddLog(LOGE, FL("Not in Connected state!")); |
| return -ENOTSUPP; |
| } |
| } |
| |
| bpf_set_offload = vos_mem_malloc(sizeof(*bpf_set_offload)); |
| if (bpf_set_offload == NULL) { |
| hddLog(LOGE, FL("vos_mem_malloc failed for bpf_set_offload")); |
| return -ENOMEM; |
| } |
| vos_mem_zero(bpf_set_offload, sizeof(*bpf_set_offload)); |
| |
| /* Parse and fetch bpf packet size */ |
| if (!tb[BPF_PACKET_SIZE]) { |
| hddLog(LOGE, FL("attr bpf packet size failed")); |
| goto fail; |
| } |
| bpf_set_offload->total_length = nla_get_u32(tb[BPF_PACKET_SIZE]); |
| |
| if (!bpf_set_offload->total_length) { |
| hddLog(LOG1, FL("BPF reset packet filter received")); |
| goto post_sme; |
| } |
| |
| /* Parse and fetch bpf program */ |
| if (!tb[BPF_PROGRAM]) { |
| hddLog(LOGE, FL("attr bpf program failed")); |
| goto fail; |
| } |
| |
| prog_len = nla_len(tb[BPF_PROGRAM]); |
| bpf_set_offload->program = vos_mem_malloc(sizeof(uint8_t) * prog_len); |
| if (!bpf_set_offload->program) { |
| hddLog(LOGE, FL("failed to allocate memory for bpf filter")); |
| ret_val = -ENOMEM; |
| goto fail; |
| } |
| bpf_set_offload->current_length = prog_len; |
| nla_memcpy(bpf_set_offload->program, tb[BPF_PROGRAM], prog_len); |
| bpf_set_offload->session_id = adapter->sessionId; |
| |
| hddLog(LOG1, FL("BPF set instructions")); |
| VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, |
| bpf_set_offload->program, prog_len); |
| |
| /* Parse and fetch filter Id */ |
| if (!tb[BPF_FILTER_ID]) { |
| hddLog(LOGE, FL("attr filter id failed")); |
| goto fail; |
| } |
| bpf_set_offload->filter_id = nla_get_u32(tb[BPF_FILTER_ID]); |
| |
| /* Parse and fetch current offset */ |
| if (!tb[BPF_CURRENT_OFFSET]) { |
| hddLog(LOGE, FL("attr current offset failed")); |
| goto fail; |
| } |
| bpf_set_offload->current_offset = nla_get_u32(tb[BPF_CURRENT_OFFSET]); |
| |
| post_sme: |
| hddLog(LOG1, FL("Posting BPF SET/RESET to SME, session_id: %d Bpf Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d"), |
| bpf_set_offload->session_id, |
| bpf_set_offload->version, |
| bpf_set_offload->filter_id, |
| bpf_set_offload->total_length, |
| bpf_set_offload->current_length, |
| bpf_set_offload->current_offset); |
| |
| hstatus = sme_set_bpf_instructions(hdd_ctx->hHal, bpf_set_offload); |
| if (!HAL_STATUS_SUCCESS(hstatus)) { |
| hddLog(LOGE, |
| FL("sme_set_bpf_instructions failed(err=%d)"), hstatus); |
| goto fail; |
| } |
| EXIT(); |
| if (bpf_set_offload->current_length) |
| vos_mem_free(bpf_set_offload->program); |
| vos_mem_free(bpf_set_offload); |
| return 0; |
| |
| fail: |
| if (bpf_set_offload->current_length) |
| vos_mem_free(bpf_set_offload->program); |
| vos_mem_free(bpf_set_offload); |
| return ret_val; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_bpf_offload() - Set/Reset to BPF Offload |
| * @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_bpf_offload(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[BPF_MAX + 1]; |
| int ret_val, packet_filter_subcmd; |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| if (!hdd_ctx->bpf_enabled) { |
| hddLog(LOGE, FL("BPF offload is not supported/enabled")); |
| return -ENOTSUPP; |
| } |
| |
| if (nla_parse(tb, BPF_MAX, data, data_len, |
| wlan_hdd_bpf_offload_policy)) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| if (!tb[BPF_SET_RESET]) { |
| hddLog(LOGE, FL("attr bpf set reset failed")); |
| return -EINVAL; |
| } |
| |
| packet_filter_subcmd = nla_get_u32(tb[BPF_SET_RESET]); |
| |
| if (packet_filter_subcmd == QCA_WLAN_GET_PACKET_FILTER) |
| return hdd_get_bpf_offload(hdd_ctx); |
| else |
| return hdd_set_reset_bpf_offload(hdd_ctx, tb, |
| pAdapter); |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_bpf_offload() - SSR Wrapper to BPF Offload |
| * @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_bpf_offload(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_bpf_offload(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_init_bpf_completion() - Initialize the completion event for bpf |
| * |
| * Return: None |
| */ |
| void hdd_init_bpf_completion(void) |
| { |
| init_completion(&bpf_context.completion); |
| } |
| |
| #undef BPF_INVALID |
| #undef BPF_SET_RESET |
| #undef BPF_VERSION |
| #undef BPF_ID |
| #undef BPF_PACKET_SIZE |
| #undef BPF_CURRENT_OFFSET |
| #undef BPF_PROGRAM |
| #undef BPF_MAX |
| |
| /** |
| * __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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| int ret_val; |
| struct sk_buff *skb; |
| uint32_t nl_buf_len; |
| |
| ENTER(); |
| |
| ret_val = wlan_hdd_validate_context(hdd_ctx); |
| if (ret_val) |
| return ret_val; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| hddLog(LOG1, "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) { |
| hddLog(LOGE, FL("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)) { |
| hddLog(LOGE, FL("nla put failure")); |
| goto nla_put_failure; |
| } |
| cfg80211_vendor_cmd_reply(skb); |
| 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_bus_size(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * define short names for the global vendor params |
| * used by wlan_hdd_cfg80211_wakelock_stats_rsp_callback() |
| */ |
| #define PARAM_TOTAL_CMD_EVENT_WAKE \ |
| QCA_WLAN_VENDOR_ATTR_TOTAL_CMD_EVENT_WAKE |
| #define PARAM_CMD_EVENT_WAKE_CNT_PTR \ |
| QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_PTR |
| #define PARAM_CMD_EVENT_WAKE_CNT_SZ \ |
| QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_SZ |
| #define PARAM_TOTAL_DRIVER_FW_LOCAL_WAKE \ |
| QCA_WLAN_VENDOR_ATTR_TOTAL_DRIVER_FW_LOCAL_WAKE |
| #define PARAM_DRIVER_FW_LOCAL_WAKE_CNT_PTR \ |
| QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_PTR |
| #define PARAM_DRIVER_FW_LOCAL_WAKE_CNT_SZ \ |
| QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_SZ |
| #define PARAM_TOTAL_RX_DATA_WAKE \ |
| QCA_WLAN_VENDOR_ATTR_TOTAL_RX_DATA_WAKE |
| #define PARAM_RX_UNICAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_RX_UNICAST_CNT |
| #define PARAM_RX_MULTICAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_RX_MULTICAST_CNT |
| #define PARAM_RX_BROADCAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_RX_BROADCAST_CNT |
| #define PARAM_ICMP_PKT \ |
| QCA_WLAN_VENDOR_ATTR_ICMP_PKT |
| #define PARAM_ICMP6_PKT \ |
| QCA_WLAN_VENDOR_ATTR_ICMP6_PKT |
| #define PARAM_ICMP6_RA \ |
| QCA_WLAN_VENDOR_ATTR_ICMP6_RA |
| #define PARAM_ICMP6_NA \ |
| QCA_WLAN_VENDOR_ATTR_ICMP6_NA |
| #define PARAM_ICMP6_NS \ |
| QCA_WLAN_VENDOR_ATTR_ICMP6_NS |
| #define PARAM_ICMP4_RX_MULTICAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_ICMP4_RX_MULTICAST_CNT |
| #define PARAM_ICMP6_RX_MULTICAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_ICMP6_RX_MULTICAST_CNT |
| #define PARAM_OTHER_RX_MULTICAST_CNT \ |
| QCA_WLAN_VENDOR_ATTR_OTHER_RX_MULTICAST_CNT |
| #define PARAM_RSSI_BREACH_CNT \ |
| QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT |
| #define PARAM_LOW_RSSI_CNT \ |
| QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT |
| #define PARAM_GSCAN_CNT \ |
| QCA_WLAN_VENDOR_ATTR_GSCAN_CNT |
| #define PARAM_PNO_COMPLETE_CNT \ |
| QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT |
| #define PARAM_PNO_MATCH_CNT \ |
| QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT |
| |
| |
| /** |
| * hdd_send_wakelock_stats() - API to send wakelock stats |
| * @ctx: context to be passed to callback |
| * @data: data passed to callback |
| * |
| * This function is used to send wake lock stats to HAL layer |
| * |
| * Return: 0 on success, error number otherwise. |
| */ |
| static uint32_t hdd_send_wakelock_stats(hdd_context_t *hdd_ctx, |
| const struct sir_wake_lock_stats *data) |
| { |
| struct sk_buff *skb; |
| uint32_t nl_buf_len; |
| uint32_t total_rx_data_wake, rx_multicast_cnt; |
| uint32_t ipv6_rx_multicast_addr_cnt; |
| uint32_t icmpv6_cnt; |
| |
| ENTER(); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += |
| QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX * |
| (NLMSG_HDRLEN + sizeof(uint32_t)); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| hddLog(LOG1, "wow_ucast_wake_up_count %d", |
| data->wow_ucast_wake_up_count); |
| hddLog(LOG1, "wow_bcast_wake_up_count %d", |
| data->wow_bcast_wake_up_count); |
| hddLog(LOG1, "wow_ipv4_mcast_wake_up_count %d", |
| data->wow_ipv4_mcast_wake_up_count); |
| hddLog(LOG1, "wow_ipv6_mcast_wake_up_count %d", |
| data->wow_ipv6_mcast_wake_up_count); |
| hddLog(LOG1, "wow_ipv6_mcast_ra_stats %d", |
| data->wow_ipv6_mcast_ra_stats); |
| hddLog(LOG1, "wow_ipv6_mcast_ns_stats %d", |
| data->wow_ipv6_mcast_ns_stats); |
| hddLog(LOG1, "wow_ipv6_mcast_na_stats %d", |
| data->wow_ipv6_mcast_na_stats); |
| hddLog(LOG1, "wow_icmpv4_count %d", data->wow_icmpv4_count); |
| hddLog(LOG1, "wow_icmpv6_count %d", data->wow_icmpv6_count); |
| hddLog(LOG1, "wow_rssi_breach_wake_up_count %d", |
| data->wow_rssi_breach_wake_up_count); |
| hddLog(LOG1, "wow_low_rssi_wake_up_count %d", |
| data->wow_low_rssi_wake_up_count); |
| hddLog(LOG1, "wow_gscan_wake_up_count %d", |
| data->wow_gscan_wake_up_count); |
| hddLog(LOG1, "wow_pno_complete_wake_up_count %d", |
| data->wow_pno_complete_wake_up_count); |
| hddLog(LOG1, "wow_pno_match_wake_up_count %d", |
| data->wow_pno_match_wake_up_count); |
| |
| ipv6_rx_multicast_addr_cnt = |
| data->wow_ipv6_mcast_wake_up_count; |
| |
| icmpv6_cnt = |
| data->wow_icmpv6_count; |
| |
| rx_multicast_cnt = |
| data->wow_ipv4_mcast_wake_up_count + |
| ipv6_rx_multicast_addr_cnt; |
| |
| total_rx_data_wake = |
| data->wow_ucast_wake_up_count + |
| data->wow_bcast_wake_up_count + |
| rx_multicast_cnt; |
| |
| if (nla_put_u32(skb, PARAM_TOTAL_CMD_EVENT_WAKE, 0) || |
| nla_put_u32(skb, PARAM_CMD_EVENT_WAKE_CNT_PTR, 0) || |
| nla_put_u32(skb, PARAM_CMD_EVENT_WAKE_CNT_SZ, 0) || |
| nla_put_u32(skb, PARAM_TOTAL_DRIVER_FW_LOCAL_WAKE, 0) || |
| nla_put_u32(skb, PARAM_DRIVER_FW_LOCAL_WAKE_CNT_PTR, 0) || |
| nla_put_u32(skb, PARAM_DRIVER_FW_LOCAL_WAKE_CNT_SZ, 0) || |
| nla_put_u32(skb, PARAM_TOTAL_RX_DATA_WAKE, |
| total_rx_data_wake) || |
| nla_put_u32(skb, PARAM_RX_UNICAST_CNT, |
| data->wow_ucast_wake_up_count) || |
| nla_put_u32(skb, PARAM_RX_MULTICAST_CNT, |
| rx_multicast_cnt) || |
| nla_put_u32(skb, PARAM_RX_BROADCAST_CNT, |
| data->wow_bcast_wake_up_count) || |
| nla_put_u32(skb, PARAM_ICMP_PKT, |
| data->wow_icmpv4_count) || |
| nla_put_u32(skb, PARAM_ICMP6_PKT, |
| icmpv6_cnt) || |
| nla_put_u32(skb, PARAM_ICMP6_RA, |
| data->wow_ipv6_mcast_ra_stats) || |
| nla_put_u32(skb, PARAM_ICMP6_NA, |
| data->wow_ipv6_mcast_na_stats) || |
| nla_put_u32(skb, PARAM_ICMP6_NS, |
| data->wow_ipv6_mcast_ns_stats) || |
| nla_put_u32(skb, PARAM_ICMP4_RX_MULTICAST_CNT, |
| data->wow_ipv4_mcast_wake_up_count) || |
| nla_put_u32(skb, PARAM_ICMP6_RX_MULTICAST_CNT, |
| ipv6_rx_multicast_addr_cnt) || |
| nla_put_u32(skb, PARAM_OTHER_RX_MULTICAST_CNT, 0) || |
| nla_put_u32(skb, PARAM_RSSI_BREACH_CNT, |
| data->wow_rssi_breach_wake_up_count) || |
| nla_put_u32(skb, PARAM_LOW_RSSI_CNT, |
| data->wow_low_rssi_wake_up_count) || |
| nla_put_u32(skb, PARAM_GSCAN_CNT, |
| data->wow_gscan_wake_up_count) || |
| nla_put_u32(skb, PARAM_PNO_COMPLETE_CNT, |
| data->wow_pno_complete_wake_up_count) || |
| nla_put_u32(skb, PARAM_PNO_MATCH_CNT, |
| data->wow_pno_match_wake_up_count)) { |
| hddLog(LOGE, FL("nla put fail")); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| |
| EXIT(); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * __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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| eHalStatus status; |
| int ret; |
| struct sir_wake_lock_stats wake_lock_stats; |
| VOS_STATUS vos_status; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| return -EINVAL; |
| |
| vos_status = wma_get_wakelock_stats(&wake_lock_stats); |
| if (vos_status != VOS_STATUS_SUCCESS) { |
| hddLog(LOGE, |
| FL("failed to get wakelock stats(err=%d)"), vos_status); |
| return -EINVAL; |
| } |
| |
| ret = hdd_send_wakelock_stats(hdd_ctx, |
| &wake_lock_stats); |
| if (ret) |
| hddLog(LOGE, FL("Failed to post wake lock stats")); |
| |
| 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_wakelock_stats(wiphy, wdev, data, |
| data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static 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 }, |
| }; |
| |
| /** |
| * __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) |
| { |
| hdd_context_t *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; |
| int channel_hint = 0; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX, |
| data, data_len, |
| wlan_hdd_set_acs_dfs_config_policy)) { |
| hddLog(LOGE, FL("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)) { |
| hddLog(LOGE, FL("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_CHANNEL_HINT]) |
| channel_hint = nla_get_u8( |
| tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]); |
| |
| if (!IS_CHANNEL_VALID(channel_hint)) { |
| hddLog(LOGE, FL("acs channel is not valid")); |
| return -EINVAL; |
| } |
| acs_policy->acs_channel = channel_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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_acs_dfs_mode(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| /** |
| * 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 CSR_STA_ROAM_POLICY_DFS_ENABLED; |
| break; |
| case DFS_MODE_DISABLE: |
| return CSR_STA_ROAM_POLICY_DFS_DISABLED; |
| break; |
| case DFS_MODE_DEPRIORITIZE: |
| return CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE; |
| break; |
| default: |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("STA Roam policy dfs mode is NONE")); |
| return CSR_STA_ROAM_POLICY_NONE; |
| } |
| } |
| /* |
| * hdd_get_sap_operating_channel: Get current operating channel |
| * for sap. |
| * @hdd_ctx: hdd context |
| * |
| * Return : Corresponding band for SAP operating channel |
| */ |
| uint8_t hdd_get_sap_operating_channel(hdd_context_t *hdd_ctx) { |
| hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL; |
| VOS_STATUS status; |
| hdd_adapter_t *adapter; |
| uint8_t operating_channel = 0; |
| uint8_t sap_operating_band = 0; |
| status = hdd_get_front_adapter(hdd_ctx, &adapter_node); |
| while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) { |
| adapter = adapter_node->pAdapter; |
| |
| if (!(adapter && (WLAN_HDD_SOFTAP == adapter->device_mode))) { |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, |
| &next); |
| adapter_node = next; |
| continue; |
| } |
| operating_channel = adapter->sessionCtx.ap.operatingChannel; |
| if (IS_24G_CH(operating_channel)) |
| sap_operating_band = eCSR_BAND_24; |
| else if(IS_5G_CH(operating_channel)) |
| sap_operating_band = eCSR_BAND_5G; |
| else sap_operating_band = eCSR_BAND_ALL; |
| status = hdd_get_next_adapter(hdd_ctx, adapter_node, |
| &next); |
| adapter_node = next; |
| } |
| return sap_operating_band; |
| } |
| |
| static 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; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| hdd_context_t *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; |
| eHalStatus status; |
| uint8_t sap_operating_band = 0; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX, |
| data, data_len, |
| wlan_hdd_set_sta_roam_config_policy)) { |
| hddLog(LOGE, FL("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)) { |
| hddLog(LOGE, FL("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_channel(hdd_ctx); |
| status = sme_update_sta_roam_policy(hdd_ctx->hHal, sta_roam_dfs_mode, |
| skip_unsafe_channels, adapter->sessionId, |
| sap_operating_band); |
| |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_sta_roam_policy(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| #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) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| int ret; |
| uint16_t unsafe_channel_count; |
| int unsafe_channel_index; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| vos_get_wlan_unsafe_channel(hdd_ctx->unsafe_channel_list, |
| &(hdd_ctx->unsafe_channel_count), |
| sizeof(hdd_ctx->unsafe_channel_list)); |
| |
| unsafe_channel_count = VOS_MIN((uint16_t)hdd_ctx->unsafe_channel_count, |
| (uint16_t)NUM_20MHZ_RF_CHANNELS); |
| for (unsafe_channel_index = 0; |
| unsafe_channel_index < unsafe_channel_count; |
| unsafe_channel_index++) { |
| hddLog(LOGE, FL("Channel %d is not safe. "), |
| hdd_ctx->unsafe_channel_list[unsafe_channel_index]); |
| } |
| hdd_unsafe_channel_restart_sap(hdd_ctx); |
| 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) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_avoid_freq(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| #endif |
| |
| static 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 }, |
| }; |
| |
| /* |
| * __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; |
| hdd_adapter_t *hostapd_adapter = WLAN_HDD_GET_PRIV_PTR(ndev); |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1]; |
| uint8_t config_channel = 0; |
| hdd_ap_ctx_t *ap_ctx; |
| int ret; |
| |
| ENTER(); |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return -EINVAL; |
| |
| if (!test_bit(SOFTAP_BSS_STARTED, &hostapd_adapter->event_flags)) { |
| hddLog(LOGE, FL("SAP is not started yet. Restart sap will be invalid")); |
| return -EINVAL; |
| } |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX, |
| data, data_len, |
| wlan_hdd_sap_config_policy)) { |
| hddLog(LOGE, FL("invalid attr")); |
| return -EINVAL; |
| } |
| |
| if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]) |
| config_channel = |
| nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]); |
| |
| if (!IS_CHANNEL_VALID(config_channel)) { |
| hddLog(LOGE, FL("Channel %d is not valid to restart SAP"), |
| config_channel); |
| return -ENOTSUPP; |
| } |
| ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter); |
| ap_ctx->sapConfig.channel = config_channel; |
| sme_SelectCBMode(hdd_ctx->hHal, |
| ap_ctx->sapConfig.SapHw_mode, |
| ap_ctx->sapConfig.channel, |
| ap_ctx->sapConfig.sec_ch, |
| &ap_ctx->sapConfig.vht_channel_width, |
| ap_ctx->sapConfig.ch_width_orig); |
| |
| wlan_hdd_restart_sap(hostapd_adapter); |
| |
| 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_sap_configuration_set(wiphy, |
| wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * define short names for the global vendor params |
| * used by __wlan_hdd_cfg80211_get_station_cmd() |
| */ |
| #define STATION_INVALID \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INVALID |
| #define STATION_INFO \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO |
| #define STATION_ASSOC_FAIL_REASON \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_ASSOC_FAIL_REASON |
| #define STATION_REMOTE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_REMOTE |
| #define STATION_MAX \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX |
| |
| static const struct nla_policy |
| hdd_get_station_policy[STATION_MAX + 1] = { |
| [STATION_INFO] = {.type = NLA_FLAG}, |
| [STATION_ASSOC_FAIL_REASON] = {.type = NLA_FLAG}, |
| }; |
| |
| /* define short names for get station info attributes */ |
| #define LINK_INFO_STANDARD_NL80211_ATTR \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_LINK_STANDARD_NL80211_ATTR |
| #define AP_INFO_STANDARD_NL80211_ATTR \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_STANDARD_NL80211_ATTR |
| #define INFO_ROAM_COUNT \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ROAM_COUNT |
| #define INFO_AKM \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AKM |
| #define WLAN802_11_MODE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_802_11_MODE |
| #define AP_INFO_HS20_INDICATION \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_HS20_INDICATION |
| #define HT_OPERATION \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HT_OPERATION |
| #define VHT_OPERATION \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_VHT_OPERATION |
| #define INFO_ASSOC_FAIL_REASON \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_FAIL_REASON |
| #define REMOTE_MAX_PHY_RATE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_MAX_PHY_RATE |
| #define REMOTE_TX_PACKETS \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_PACKETS |
| #define REMOTE_TX_BYTES \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_BYTES |
| #define REMOTE_RX_PACKETS \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_PACKETS |
| #define REMOTE_RX_BYTES \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BYTES |
| #define REMOTE_LAST_TX_RATE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_TX_RATE |
| #define REMOTE_LAST_RX_RATE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_RX_RATE |
| #define REMOTE_WMM \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_WMM |
| #define REMOTE_SUPPORTED_MODE \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SUPPORTED_MODE |
| #define REMOTE_AMPDU \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AMPDU |
| #define REMOTE_TX_STBC \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_STBC |
| #define REMOTE_RX_STBC \ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_STBC |
| #define REMOTE_CH_WIDTH\ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_CH_WIDTH |
| #define REMOTE_SGI_ENABLE\ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SGI_ENABLE |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
| #define REMOTE_PAD\ |
| QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_PAD |
| #endif |
| |
| /** |
| * hdd_get_station_assoc_fail() - Handle get station assoc fail |
| * @hdd_ctx: HDD context within host driver |
| * @wdev: wireless device |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION_ASSOC_FAIL. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_get_station_assoc_fail(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| struct sk_buff *skb = NULL; |
| uint32_t nl_buf_len; |
| hdd_station_ctx_t *hdd_sta_ctx; |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += sizeof(uint32_t); |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| if (nla_put_u32(skb, INFO_ASSOC_FAIL_REASON, |
| hdd_sta_ctx->conn_info.assoc_status_code)) { |
| hddLog(LOGE, FL("put fail assoc status code")); |
| goto fail; |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| #ifdef FEATURE_WLAN_ESE |
| /** |
| * hdd_check_cckm_auth_type() - check cckm auth type |
| * @auth_type: csr auth type |
| * |
| * Return: auth type |
| */ |
| static int hdd_check_cckm_auth_type(uint32_t auth_type) |
| { |
| uint32_t ret_val = QCA_WLAN_AUTH_TYPE_INVALID; |
| |
| if (auth_type == eCSR_AUTH_TYPE_CCKM_WPA) |
| ret_val = QCA_WLAN_AUTH_TYPE_CCKM_WPA; |
| else if (auth_type == eCSR_AUTH_TYPE_CCKM_RSN) |
| ret_val = QCA_WLAN_AUTH_TYPE_CCKM_RSN; |
| return ret_val; |
| } |
| #else |
| static int hdd_check_cckm_auth_type(uint32_t auth_type) |
| { |
| return QCA_WLAN_AUTH_TYPE_INVALID; |
| } |
| #endif |
| |
| /** |
| * hdd_map_auth_type() - transform auth type specific to |
| * vendor command |
| * @auth_type: csr auth type |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_convert_auth_type(uint32_t auth_type) |
| { |
| uint32_t ret_val; |
| |
| ret_val = hdd_check_cckm_auth_type(auth_type); |
| if (ret_val != QCA_WLAN_AUTH_TYPE_INVALID) |
| return ret_val; |
| |
| switch (auth_type) { |
| case eCSR_AUTH_TYPE_OPEN_SYSTEM: |
| ret_val = QCA_WLAN_AUTH_TYPE_OPEN; |
| break; |
| case eCSR_AUTH_TYPE_SHARED_KEY: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHARED; |
| break; |
| case eCSR_AUTH_TYPE_WPA: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA; |
| break; |
| case eCSR_AUTH_TYPE_WPA_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA_PSK; |
| break; |
| case eCSR_AUTH_TYPE_AUTOSWITCH: |
| ret_val = QCA_WLAN_AUTH_TYPE_AUTOSWITCH; |
| break; |
| case eCSR_AUTH_TYPE_WPA_NONE: |
| ret_val = QCA_WLAN_AUTH_TYPE_WPA_NONE; |
| break; |
| case eCSR_AUTH_TYPE_RSN: |
| ret_val = QCA_WLAN_AUTH_TYPE_RSN; |
| break; |
| case eCSR_AUTH_TYPE_RSN_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_RSN_PSK; |
| break; |
| case eCSR_AUTH_TYPE_FT_RSN: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT; |
| break; |
| case eCSR_AUTH_TYPE_FT_RSN_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_FT_PSK; |
| break; |
| case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE: |
| ret_val = QCA_WLAN_AUTH_TYPE_WAI; |
| break; |
| case eCSR_AUTH_TYPE_WAPI_WAI_PSK: |
| ret_val = QCA_WLAN_AUTH_TYPE_WAI_PSK; |
| break; |
| case eCSR_AUTH_TYPE_RSN_PSK_SHA256: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHA256_PSK; |
| break; |
| case eCSR_AUTH_TYPE_RSN_8021X_SHA256: |
| ret_val = QCA_WLAN_AUTH_TYPE_SHA256; |
| break; |
| case eCSR_NUM_OF_SUPPORT_AUTH_TYPE: |
| case eCSR_AUTH_TYPE_FAILED: |
| case eCSR_AUTH_TYPE_NONE: |
| default: |
| ret_val = QCA_WLAN_AUTH_TYPE_INVALID; |
| break; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * hdd_map_dot_11_mode() - transform dot11mode type specific to |
| * vendor command |
| * @dot11mode: dot11mode |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int hdd_convert_dot11mode(uint32_t dot11mode) |
| { |
| uint32_t ret_val; |
| |
| switch (dot11mode) { |
| case eCSR_CFG_DOT11_MODE_11A: |
| ret_val = QCA_WLAN_802_11_MODE_11A; |
| break; |
| case eCSR_CFG_DOT11_MODE_11B: |
| ret_val = QCA_WLAN_802_11_MODE_11B; |
| break; |
| case eCSR_CFG_DOT11_MODE_11G: |
| ret_val = QCA_WLAN_802_11_MODE_11G; |
| break; |
| case eCSR_CFG_DOT11_MODE_11N: |
| ret_val = QCA_WLAN_802_11_MODE_11N; |
| break; |
| case eCSR_CFG_DOT11_MODE_11AC: |
| ret_val = QCA_WLAN_802_11_MODE_11AC; |
| break; |
| case eCSR_CFG_DOT11_MODE_AUTO: |
| case eCSR_CFG_DOT11_MODE_ABG: |
| default: |
| ret_val = QCA_WLAN_802_11_MODE_INVALID; |
| } |
| return ret_val; |
| } |
| |
| /** |
| * hdd_add_tx_bitrate() - add tx bitrate attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_tx_bitrate(struct sk_buff *skb, |
| hdd_station_ctx_t *hdd_sta_ctx, |
| int idx) |
| { |
| struct nlattr *nla_attr; |
| uint32_t bitrate, bitrate_compat; |
| struct rate_info txrate; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
| txrate.flags = hdd_sta_ctx->conn_info.txrate.flags; |
| txrate.mcs = hdd_sta_ctx->conn_info.txrate.mcs; |
| txrate.legacy = hdd_sta_ctx->conn_info.txrate.legacy; |
| txrate.nss = hdd_sta_ctx->conn_info.txrate.nss; |
| |
| bitrate = cfg80211_calculate_bitrate(&txrate); |
| |
| hdd_sta_ctx->conn_info.txrate.flags = txrate.flags; |
| hdd_sta_ctx->conn_info.txrate.mcs = txrate.mcs; |
| hdd_sta_ctx->conn_info.txrate.legacy = txrate.legacy; |
| hdd_sta_ctx->conn_info.txrate.nss = txrate.nss; |
| |
| /* report 16-bit bitrate only if we can */ |
| bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; |
| if (bitrate > 0 && |
| nla_put_u32(skb, NL80211_RATE_INFO_BITRATE32, bitrate)) { |
| hddLog(LOGE, FL("put fail bitrate32")); |
| goto fail; |
| } |
| if (bitrate_compat > 0 && |
| nla_put_u16(skb, NL80211_RATE_INFO_BITRATE, bitrate_compat)) { |
| hddLog(LOGE, FL("put fail bitrate")); |
| goto fail; |
| } |
| if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS, |
| hdd_sta_ctx->conn_info.txrate.nss)) { |
| hddLog(LOGE, FL("put fail nss")); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_sta_info() - add station info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_sta_info(struct sk_buff *skb, |
| hdd_station_ctx_t *hdd_sta_ctx, int idx) |
| { |
| struct nlattr *nla_attr; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, |
| (hdd_sta_ctx->conn_info.signal + 100))) { |
| hddLog(LOGE, FL("put fail signal")); |
| goto fail; |
| } |
| if (hdd_add_tx_bitrate(skb, hdd_sta_ctx, NL80211_STA_INFO_TX_BITRATE)) |
| goto fail; |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_survey_info() - add survey info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t hdd_add_survey_info(struct sk_buff *skb, |
| hdd_station_ctx_t *hdd_sta_ctx, |
| int idx) |
| { |
| struct nlattr *nla_attr; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY, |
| hdd_sta_ctx->conn_info.freq) || |
| nla_put_u8(skb, NL80211_SURVEY_INFO_NOISE, |
| (hdd_sta_ctx->conn_info.noise + 100))) { |
| hddLog(LOGE, FL("put fail noise")); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_link_standard_info() - add link info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_add_link_standard_info(struct sk_buff *skb, |
| hdd_station_ctx_t *hdd_sta_ctx, int idx) |
| { |
| struct nlattr *nla_attr; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| if (nla_put(skb, |
| NL80211_ATTR_SSID, |
| hdd_sta_ctx->conn_info.last_ssid.SSID.length, |
| hdd_sta_ctx->conn_info.last_ssid.SSID.ssId)) { |
| hddLog(LOGE, FL("put fail ssid")); |
| goto fail; |
| } |
| if (hdd_add_survey_info(skb, hdd_sta_ctx, NL80211_ATTR_SURVEY_INFO)) |
| goto fail; |
| if (hdd_add_sta_info(skb, hdd_sta_ctx, NL80211_ATTR_STA_INFO)) |
| goto fail; |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_add_ap_standard_info() - add ap info attribute |
| * @skb: pointer to sk buff |
| * @hdd_sta_ctx: pointer to hdd station context |
| * @idx: attribute index |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_add_ap_standard_info(struct sk_buff *skb, |
| hdd_station_ctx_t *hdd_sta_ctx, int idx) |
| { |
| struct nlattr *nla_attr; |
| |
| nla_attr = nla_nest_start(skb, idx); |
| if (!nla_attr) |
| goto fail; |
| if (hdd_sta_ctx->conn_info.conn_flag.vht_present) |
| if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY, |
| sizeof(hdd_sta_ctx->conn_info.vht_caps), |
| &hdd_sta_ctx->conn_info.vht_caps)) { |
| hddLog(LOGE, FL("put fail vht cap")); |
| goto fail; |
| } |
| if (hdd_sta_ctx->conn_info.conn_flag.ht_present) |
| if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY, |
| sizeof(hdd_sta_ctx->conn_info.ht_caps), |
| &hdd_sta_ctx->conn_info.ht_caps)) { |
| hddLog(LOGE, FL("put fail ht cap")); |
| goto fail; |
| } |
| nla_nest_end(skb, nla_attr); |
| return 0; |
| fail: |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_get_station_info() - send BSS information to supplicant |
| * @hdd_ctx: pointer to hdd context |
| * @adapter: pointer to adapter |
| * |
| * Return: 0 if success else error status |
| */ |
| static int hdd_get_station_info(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter) |
| { |
| struct sk_buff *skb = NULL; |
| uint8_t *tmp_hs20 = NULL; |
| uint32_t nl_buf_len; |
| hdd_station_ctx_t *hdd_sta_ctx; |
| |
| hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += sizeof(hdd_sta_ctx->conn_info.last_ssid.SSID.length) + |
| sizeof(hdd_sta_ctx->conn_info.freq) + |
| sizeof(hdd_sta_ctx->conn_info.noise) + |
| sizeof(hdd_sta_ctx->conn_info.signal) + |
| (sizeof(uint32_t) * 2) + |
| sizeof(hdd_sta_ctx->conn_info.txrate.nss) + |
| sizeof(hdd_sta_ctx->conn_info.roam_count) + |
| sizeof(hdd_sta_ctx->conn_info.last_auth_type) + |
| sizeof(hdd_sta_ctx->conn_info.dot11Mode); |
| if (hdd_sta_ctx->conn_info.conn_flag.vht_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->conn_info.vht_caps); |
| if (hdd_sta_ctx->conn_info.conn_flag.ht_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->conn_info.ht_caps); |
| if (hdd_sta_ctx->conn_info.conn_flag.hs20_present) { |
| tmp_hs20 = (uint8_t *)&(hdd_sta_ctx->conn_info.hs20vendor_ie); |
| nl_buf_len += (sizeof(hdd_sta_ctx->conn_info.hs20vendor_ie) - |
| 1); |
| } |
| if (hdd_sta_ctx->conn_info.conn_flag.ht_op_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->conn_info.ht_operation); |
| if (hdd_sta_ctx->conn_info.conn_flag.vht_op_present) |
| nl_buf_len += sizeof(hdd_sta_ctx->conn_info.vht_operation); |
| |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| return -ENOMEM; |
| } |
| |
| if (hdd_add_link_standard_info(skb, hdd_sta_ctx, |
| LINK_INFO_STANDARD_NL80211_ATTR)) { |
| hddLog(LOGE, FL("put fail link standard info")); |
| goto fail; |
| } |
| if (hdd_add_ap_standard_info(skb, hdd_sta_ctx, |
| AP_INFO_STANDARD_NL80211_ATTR)) { |
| hddLog(LOGE, FL("put fail ap standard info")); |
| goto fail; |
| } |
| if (nla_put_u32(skb, INFO_ROAM_COUNT, |
| hdd_sta_ctx->conn_info.roam_count) || |
| nla_put_u32(skb, INFO_AKM, |
| hdd_convert_auth_type( |
| hdd_sta_ctx->conn_info.last_auth_type)) || |
| nla_put_u32(skb, WLAN802_11_MODE, |
| hdd_convert_dot11mode( |
| hdd_sta_ctx->conn_info.dot11Mode))) { |
| hddLog(LOGE, FL("put fail roam_count, etc.")); |
| goto fail; |
| } |
| if (hdd_sta_ctx->conn_info.conn_flag.ht_op_present) |
| if (nla_put(skb, HT_OPERATION, |
| (sizeof(hdd_sta_ctx->conn_info.ht_operation)), |
| &hdd_sta_ctx->conn_info.ht_operation)) { |
| hddLog(LOGE, FL("put fail HT oper")); |
| goto fail; |
| } |
| if (hdd_sta_ctx->conn_info.conn_flag.vht_op_present) |
| if (nla_put(skb, VHT_OPERATION, |
| (sizeof(hdd_sta_ctx->conn_info.vht_operation)), |
| &hdd_sta_ctx->conn_info.vht_operation)) { |
| hddLog(LOGE, FL("put fail VHT oper")); |
| goto fail; |
| } |
| if (hdd_sta_ctx->conn_info.conn_flag.hs20_present) |
| if (nla_put(skb, AP_INFO_HS20_INDICATION, |
| (sizeof(hdd_sta_ctx->conn_info.hs20vendor_ie) - 1), |
| tmp_hs20 + 1)) { |
| hddLog(LOGE, FL("put fail HS20 IND")); |
| goto fail; |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * hdd_get_peer_txrx_rate_cb() - get station's txrx rate callback |
| * @peer_info: pointer of peer information |
| * @context: get peer info callback context |
| * |
| * This function fill txrx rate information to aStaInfo[staid] of hostapd |
| * adapter |
| */ |
| static void hdd_get_peer_txrx_rate_cb(struct sir_peer_info_ext_resp *peer_info, |
| void *context) |
| { |
| struct statsContext *get_txrx_rate_context; |
| struct sir_peer_info_ext *txrx_rate = NULL; |
| hdd_adapter_t *adapter; |
| uint8_t staid; |
| |
| if ((NULL == peer_info) || (NULL == context)) { |
| |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Bad param, peer_info [%pK] context [%pK]", |
| __func__, peer_info, context); |
| return; |
| } |
| |
| spin_lock(&hdd_context_lock); |
| /* |
| * there is a race condition that exists between this callback |
| * function and the caller since the caller could time out either |
| * before or while this code is executing. we use a spinlock to |
| * serialize these actions |
| */ |
| get_txrx_rate_context = context; |
| if (PEER_INFO_CONTEXT_MAGIC != |
| get_txrx_rate_context->magic) { |
| |
| /* |
| * the caller presumably timed out so there is nothing |
| * we can do |
| */ |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_WARN, |
| "%s: Invalid context, magic [%08x]", |
| __func__, |
| get_txrx_rate_context->magic); |
| return; |
| } |
| |
| if (!peer_info->count) { |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Fail to get remote peer info")); |
| return; |
| } |
| |
| adapter = get_txrx_rate_context->pAdapter; |
| txrx_rate = peer_info->info; |
| if (VOS_STATUS_SUCCESS != hdd_softap_GetStaId(adapter, |
| (v_MACADDR_t *)txrx_rate->peer_macaddr, |
| &staid)) { |
| spin_unlock(&hdd_context_lock); |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Station MAC address does not matching", |
| __func__); |
| return; |
| } |
| |
| adapter->aStaInfo[staid].tx_rate = txrx_rate->tx_rate; |
| adapter->aStaInfo[staid].rx_rate = txrx_rate->rx_rate; |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s txrate %x rxrate %x\n", |
| __func__, |
| adapter->aStaInfo[staid].tx_rate, |
| adapter->aStaInfo[staid].rx_rate); |
| |
| get_txrx_rate_context->magic = 0; |
| |
| /* notify the caller */ |
| complete(&get_txrx_rate_context->completion); |
| |
| /* serialization is complete */ |
| spin_unlock(&hdd_context_lock); |
| |
| if (txrx_rate) |
| hddLog(VOS_TRACE_LEVEL_INFO, "%s %pM tx rate %u rx rate %u", |
| __func__, |
| txrx_rate->peer_macaddr, |
| txrx_rate->tx_rate, |
| txrx_rate->rx_rate); |
| } |
| |
| /** |
| * wlan_hdd_get_peer_txrx_rate() - get station's txrx rate |
| * @adapter: hostapd interface |
| * @macaddress: mac address of requested peer |
| * |
| * This function call sme_get_peer_info_ext to get txrx rate |
| * |
| * Return: 0 on success, otherwise error value |
| */ |
| static int wlan_hdd_get_txrx_rate(hdd_adapter_t *adapter, |
| v_MACADDR_t macaddress) |
| { |
| eHalStatus hstatus; |
| int ret; |
| struct statsContext context; |
| struct sir_peer_info_ext_req txrx_rate_req; |
| |
| if (NULL == adapter) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: pAdapter is NULL", |
| __func__); |
| return -EFAULT; |
| } |
| |
| init_completion(&context.completion); |
| context.magic = PEER_INFO_CONTEXT_MAGIC; |
| context.pAdapter = adapter; |
| |
| vos_mem_copy(&(txrx_rate_req.peer_macaddr), &macaddress, |
| VOS_MAC_ADDR_SIZE); |
| txrx_rate_req.sessionid = adapter->sessionId; |
| txrx_rate_req.reset_after_request = 0; |
| hstatus = sme_get_peer_info_ext(WLAN_HDD_GET_HAL_CTX(adapter), |
| &txrx_rate_req, |
| &context, |
| hdd_get_peer_txrx_rate_cb); |
| if (eHAL_STATUS_SUCCESS != hstatus) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: Unable to retrieve statistics for txrx_rate", |
| __func__); |
| ret = -EFAULT; |
| } else { |
| if (!wait_for_completion_timeout(&context.completion, |
| msecs_to_jiffies(WLAN_WAIT_TIME_STATS))) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: SME timed out while retrieving txrx_rate", |
| __func__); |
| ret = -EFAULT; |
| } else { |
| ret = 0; |
| } |
| } |
| /* |
| * either we never sent a request, we sent a request and received a |
| * response or we sent a request and timed out. if we never sent a |
| * request or if we sent a request and got a response, we want to |
| * clear the magic out of paranoia. if we timed out there is a |
| * race condition such that the callback function could be |
| * executing at the same time we are. of primary concern is if the |
| * callback function had already verified the "magic" but had not |
| * yet set the completion variable when a timeout occurred. we |
| * serialize these activities by invalidating the magic while |
| * holding a shared spinlock which will cause us to block if the |
| * callback is currently executing |
| */ |
| spin_lock(&hdd_context_lock); |
| context.magic = 0; |
| spin_unlock(&hdd_context_lock); |
| return ret; |
| } |
| |
| /** |
| * hdd_get_stainfo() - get stainfo for the specified peer |
| * @adapter: hostapd interface |
| * @mac_addr: mac address of requested peer |
| * |
| * This function find the stainfo for the peer with mac_addr |
| * |
| * Return: stainfo if found, NULL if not found |
| */ |
| static hdd_station_info_t *hdd_get_stainfo(hdd_adapter_t *adapter, |
| v_MACADDR_t mac_addr) |
| { |
| hdd_station_info_t *stainfo = NULL; |
| int i; |
| |
| for (i = 0; i < WLAN_MAX_STA_COUNT; i++) { |
| if (vos_mem_compare(&adapter->aStaInfo[i].macAddrSTA, |
| &mac_addr, |
| VOS_MAC_ADDR_SIZE)) |
| stainfo = &adapter->aStaInfo[i]; |
| } |
| |
| return stainfo; |
| } |
| |
| /** |
| * hdd_get_station_remote() - get remote peer's info |
| * @hdd_ctx: hdd context |
| * @adapter: hostapd interface |
| * @mac_addr: mac address of requested peer |
| * |
| * This function collect and indicate the remote peer's info |
| * |
| * Return: 0 on success, otherwise error value |
| */ |
| static int hdd_get_station_remote(hdd_context_t *hdd_ctx, |
| hdd_adapter_t *adapter, |
| v_MACADDR_t mac_addr) |
| { |
| hdd_station_info_t *stainfo = hdd_get_stainfo(adapter, mac_addr); |
| struct sk_buff *skb = NULL; |
| uint32_t nl_buf_len; |
| bool txrx_rate = true; |
| |
| if (!stainfo) { |
| hddLog(LOGE, FL("peer" MAC_ADDRESS_STR "not found"), |
| MAC_ADDR_ARRAY(mac_addr.bytes)); |
| goto fail; |
| } |
| |
| nl_buf_len = NLMSG_HDRLEN; |
| nl_buf_len += (sizeof(stainfo->max_phy_rate) + NLA_HDRLEN) + |
| (sizeof(stainfo->tx_packets) + NLA_HDRLEN) + |
| (sizeof(stainfo->tx_bytes) + NLA_HDRLEN) + |
| (sizeof(stainfo->rx_packets) + NLA_HDRLEN) + |
| (sizeof(stainfo->rx_bytes) + NLA_HDRLEN) + |
| (sizeof(stainfo->isQosEnabled) + NLA_HDRLEN) + |
| (sizeof(stainfo->mode) + NLA_HDRLEN); |
| |
| if (!hdd_ctx->cfg_ini->sap_get_peer_info || |
| wlan_hdd_get_txrx_rate(adapter, mac_addr)) { |
| hddLog(LOGE, FL("fail to get tx/rx rate")); |
| txrx_rate = false; |
| } else { |
| nl_buf_len += (sizeof(stainfo->tx_rate) + NLA_HDRLEN) + |
| (sizeof(stainfo->rx_rate) + NLA_HDRLEN); |
| } |
| |
| /* below info is only valid for HT/VHT mode */ |
| if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) |
| nl_buf_len += (sizeof(stainfo->ampdu) + NLA_HDRLEN) + |
| (sizeof(stainfo->tx_stbc) + NLA_HDRLEN) + |
| (sizeof(stainfo->rx_stbc) + NLA_HDRLEN) + |
| (sizeof(stainfo->ch_width) + NLA_HDRLEN) + |
| (sizeof(stainfo->sgi_enable) + NLA_HDRLEN); |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, FL("buflen %d hdrlen %d"), |
| nl_buf_len, NLMSG_HDRLEN); |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, |
| nl_buf_len); |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed")); |
| goto fail; |
| } |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("stainfo")); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("maxrate %x tx_pkts %x tx_bytes %llx"), |
| stainfo->max_phy_rate, stainfo->tx_packets, |
| stainfo->tx_bytes); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("rx_pkts %x rx_bytes %llx mode %x"), |
| stainfo->rx_packets, stainfo->rx_bytes, |
| stainfo->mode); |
| if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("ampdu %d tx_stbc %d rx_stbc %d"), |
| stainfo->ampdu, stainfo->tx_stbc, |
| stainfo->rx_stbc); |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("wmm %d chwidth %d sgi %d"), |
| stainfo->isQosEnabled, |
| stainfo->ch_width, |
| stainfo->sgi_enable); |
| } |
| |
| if (nla_put_u32(skb, REMOTE_MAX_PHY_RATE, stainfo->max_phy_rate) || |
| nla_put_u32(skb, REMOTE_TX_PACKETS, stainfo->tx_packets) || |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
| nla_put_u64_64bit(skb, REMOTE_TX_BYTES, stainfo->tx_bytes, |
| REMOTE_PAD) || |
| #else |
| nla_put_u64(skb, REMOTE_TX_BYTES, stainfo->tx_bytes) || |
| #endif |
| nla_put_u32(skb, REMOTE_RX_PACKETS, stainfo->rx_packets) || |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) |
| nla_put_u64_64bit(skb, REMOTE_RX_BYTES, stainfo->rx_bytes, |
| REMOTE_PAD) || |
| #else |
| nla_put_u64(skb, REMOTE_RX_BYTES, stainfo->rx_bytes) || |
| #endif |
| nla_put_u8(skb, REMOTE_WMM, stainfo->isQosEnabled) || |
| nla_put_u8(skb, REMOTE_SUPPORTED_MODE, stainfo->mode)) { |
| hddLog(LOGE, FL("put fail")); |
| goto fail; |
| } |
| |
| if (txrx_rate) { |
| if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate) || |
| nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate)) { |
| hddLog(LOGE, FL("put fail")); |
| goto fail; |
| } else { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("tx_rate %x rx_rate %x"), |
| stainfo->tx_rate, stainfo->rx_rate); |
| } |
| } |
| |
| if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) { |
| if (nla_put_u8(skb, REMOTE_AMPDU, stainfo->ampdu) || |
| nla_put_u8(skb, REMOTE_TX_STBC, stainfo->tx_stbc) || |
| nla_put_u8(skb, REMOTE_RX_STBC, stainfo->rx_stbc) || |
| nla_put_u8(skb, REMOTE_CH_WIDTH, stainfo->ch_width) || |
| nla_put_u8(skb, REMOTE_SGI_ENABLE, stainfo->sgi_enable)) { |
| hddLog(LOGE, FL("put fail")); |
| goto fail; |
| } |
| } |
| |
| return cfg80211_vendor_cmd_reply(skb); |
| |
| fail: |
| if (skb) |
| kfree_skb(skb); |
| |
| return -EINVAL; |
| } |
| |
| /** |
| * __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd |
| * @wiphy: corestack handler |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int |
| __hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + 1]; |
| int32_t status; |
| |
| ENTER(); |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| status = -EPERM; |
| goto out; |
| } |
| |
| status = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != status) |
| goto out; |
| |
| status = nla_parse(tb, STATION_MAX, data, data_len, |
| hdd_get_station_policy); |
| if (status) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| goto out; |
| } |
| |
| /* Parse and fetch Command Type*/ |
| if (tb[STATION_INFO]) { |
| status = hdd_get_station_info(hdd_ctx, adapter); |
| } else if (tb[STATION_ASSOC_FAIL_REASON]) { |
| status = hdd_get_station_assoc_fail(hdd_ctx, adapter); |
| } else if (tb[STATION_REMOTE]) { |
| v_MACADDR_t mac_addr; |
| |
| if (adapter->device_mode != WLAN_HDD_SOFTAP) { |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("invalid device_mode:%d"), |
| adapter->device_mode); |
| status = -EINVAL; |
| goto out; |
| } |
| |
| memcpy(mac_addr.bytes, |
| nla_data(tb[STATION_REMOTE]), |
| VOS_MAC_ADDRESS_LEN); |
| |
| hddLog(VOS_TRACE_LEVEL_INFO, |
| FL("STATION_REMOTE "MAC_ADDRESS_STR""), |
| MAC_ADDR_ARRAY(mac_addr.bytes)); |
| |
| status = hdd_get_station_remote(hdd_ctx, adapter, mac_addr); |
| } else { |
| hddLog(LOGE, FL("get station info cmd type failed")); |
| status = -EINVAL; |
| goto out; |
| } |
| EXIT(); |
| out: |
| return status; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd |
| * @wiphy: corestack handler |
| * @wdev: wireless device |
| * @data: data |
| * @data_len: data length |
| * |
| * Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION. |
| * Validate cmd attributes and send the station info to upper layers. |
| * |
| * Return: Success(0) or reason code for failure |
| */ |
| static int32_t |
| hdd_cfg80211_get_station_cmd(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __hdd_cfg80211_get_station_cmd(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /* |
| * 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 |
| 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 = VOS_MAC_ADDR_SIZE}, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_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_fast_roaming(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| uint32_t is_fast_roam_enabled; |
| eHalStatus status; |
| int ret; |
| hdd_station_ctx_t *hddstactx; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| |
| ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, |
| qca_wlan_vendor_attr); |
| if (ret) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return -EINVAL; |
| } |
| |
| /* Parse and fetch Enable flag */ |
| if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) { |
| hddLog(LOGE, FL("attr enable failed")); |
| return -EINVAL; |
| } |
| |
| is_fast_roam_enabled = nla_get_u32( |
| tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]); |
| hddLog(LOG1, FL("isFastRoamEnabled %d"), is_fast_roam_enabled); |
| |
| /* |
| * If framework sends pause_roam, host to send WAIT indication to |
| * framework if roaming is in progress. This can help framework to |
| * defer out-network roaming. EBUSY is used to convey wait indication. |
| */ |
| if (!is_fast_roam_enabled) { |
| if (sme_staInMiddleOfRoaming(hdd_ctx->hHal, |
| adapter->sessionId)) { |
| hddLog(LOG1, FL("Roaming in progress, do not allow disable")); |
| return -EBUSY; |
| } |
| |
| hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter); |
| if (hddstactx->hdd_ReassocScenario) { |
| hddLog(LOG1, |
| FL("Roaming in progress, so unable to disable roaming")); |
| return -EBUSY; |
| } |
| } |
| |
| /* Update roaming */ |
| status = sme_config_fast_roaming(hdd_ctx->hHal, adapter->sessionId, |
| is_fast_roam_enabled); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_config_fast_roaming (err=%d)"), status); |
| return -EINVAL; |
| } |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_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_fast_roaming() |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_fast_roaming(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_fast_roaming(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static const struct nla_policy |
| txpower_scale_policy[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE] = { .type = NLA_U8 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_txpower_scale () - txpower scaling |
| * @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_txpower_scale(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter; |
| int ret; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1]; |
| uint8_t scale_value; |
| VOS_STATUS status; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX, |
| data, data_len, txpower_scale_policy)) { |
| hddLog(LOGE, "Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]) { |
| hddLog(LOGE, "attr tx power scale failed"); |
| return -EINVAL; |
| } |
| |
| scale_value = nla_get_u8(tb |
| [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]); |
| |
| if (scale_value > MAX_TXPOWER_SCALE) { |
| hddLog(LOGE, "Invalid tx power scale level"); |
| return -EINVAL; |
| } |
| |
| status = wma_set_tx_power_scale(adapter->sessionId, scale_value); |
| |
| if (VOS_STATUS_SUCCESS != status) { |
| hddLog(LOGE, "Set tx power scale failed"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_txpower_scale () - txpower scaling |
| * @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_txpower_scale(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_txpower_scale(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static const struct nla_policy txpower_scale_decr_db_policy |
| [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB] = { .type = NLA_U8 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling |
| * @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_txpower_scale_decr_db(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter; |
| int ret; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1]; |
| uint8_t scale_value; |
| VOS_STATUS status; |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (ret) |
| return ret; |
| |
| adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX, |
| data, data_len, txpower_scale_decr_db_policy)) { |
| hddLog(LOGE, "Invalid ATTR"); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]) { |
| hddLog(LOGE, "attr tx power decrease db value failed"); |
| return -EINVAL; |
| } |
| |
| scale_value = nla_get_u8(tb |
| [QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]); |
| |
| status = wma_set_tx_power_scale_decr_db(adapter->sessionId, |
| scale_value); |
| |
| if (VOS_STATUS_SUCCESS != status) { |
| hddLog(LOGE,"Set tx power decrease db failed"); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling |
| * @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_txpower_scale_decr_db(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, |
| int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_txpower_scale_decr_db(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| /** |
| * hdd_post_get_chain_rssi_rsp - send rsp to user space |
| * @hdd_ctx: Pointer to hdd context |
| * |
| * Return: 0 for success, non-zero for failure |
| */ |
| static int hdd_post_get_chain_rssi_rsp(hdd_context_t *hdd_ctx) |
| { |
| struct sk_buff *skb = NULL; |
| struct chain_rssi_result *result = |
| &hdd_ctx->chain_rssi_context.result; |
| |
| skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, |
| (sizeof(result->chain_rssi) + NLA_HDRLEN) + |
| (sizeof(result->ant_id) + NLA_HDRLEN) + |
| NLMSG_HDRLEN); |
| |
| if (!skb) { |
| hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); |
| return -ENOMEM; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI, |
| sizeof(result->chain_rssi), |
| result->chain_rssi)) { |
| hddLog(LOGE, FL("put fail")); |
| goto nla_put_failure; |
| } |
| |
| if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO, |
| sizeof(result->ant_id), |
| result->ant_id)) { |
| hddLog(LOGE, FL("put fail")); |
| goto nla_put_failure; |
| } |
| |
| cfg80211_vendor_cmd_reply(skb); |
| return 0; |
| |
| nla_put_failure: |
| kfree_skb(skb); |
| return -EINVAL; |
| } |
| |
| /** |
| * __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 get_chain_rssi_req_params req_msg; |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct hdd_chain_rssi_context *context; |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1]; |
| eHalStatus status; |
| int retval; |
| unsigned long rc; |
| const int mac_len = sizeof(req_msg.peer_macaddr); |
| int msg_len; |
| |
| ENTER(); |
| |
| retval = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != retval) |
| return retval; |
| |
| /* nla validation doesn't do exact lengths, do the validation later */ |
| retval = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, NULL); |
| if (retval) { |
| hddLog(LOGE, FL("Invalid ATTR")); |
| return retval; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) { |
| hddLog(LOGE, FL("attr mac addr failed")); |
| return -EINVAL; |
| } |
| |
| msg_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]); |
| if (msg_len != mac_len) { |
| hddLog(LOGE, FL("Invalid mac address length: %d, expected %d"), |
| msg_len, mac_len); |
| return -ERANGE; |
| } |
| |
| memcpy(&req_msg.peer_macaddr, |
| nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), mac_len); |
| |
| spin_lock(&hdd_context_lock); |
| context = &hdd_ctx->chain_rssi_context; |
| INIT_COMPLETION(context->response_event); |
| context->ignore_result = false; |
| spin_unlock(&hdd_context_lock); |
| |
| status = sme_get_chain_rssi(hdd_ctx->hHal, &req_msg); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, |
| FL("sme_get_chain_rssi failed(err=%d)"), status); |
| return -EINVAL; |
| } |
| |
| rc = wait_for_completion_timeout(&context->response_event, |
| msecs_to_jiffies(WLAN_WAIT_TIME_CHAIN_RSSI)); |
| if (!rc) { |
| hddLog(LOGE, FL("Target response timed out")); |
| spin_lock(&hdd_context_lock); |
| context->ignore_result = true; |
| spin_unlock(&hdd_context_lock); |
| return -ETIMEDOUT; |
| } |
| |
| retval = hdd_post_get_chain_rssi_rsp(hdd_ctx); |
| if (retval) |
| hddLog(LOGE, |
| FL("Failed to send chain rssi to user space")); |
| |
| EXIT(); |
| return retval; |
| } |
| |
| /** |
| * 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 ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| static const struct |
| nla_policy |
| qca_wlan_vendor_peer_flush_pending_policy |
| [QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1] = { |
| [QCA_WLAN_VENDOR_ATTR_PEER_ADDR] = {.type = NLA_BINARY, |
| .len = VOS_MAC_ADDR_SIZE}, |
| [QCA_WLAN_VENDOR_ATTR_AC] = { .type = NLA_U8 }, |
| }; |
| |
| /** |
| * __wlan_hdd_cfg80211_peer_flush_tids() - flush peer pending packets |
| * @wiphy: Pointer to wireless phy |
| * @wdev: Pointer to wireless device |
| * @data: Pointer to data |
| * @data_len: Length of @data |
| * |
| * This function is used to flush peer pending packets using vendor commands |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int |
| __wlan_hdd_cfg80211_peer_flush_pending(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| hdd_context_t *hdd_ctx = wiphy_priv(wiphy); |
| struct net_device *dev = wdev->netdev; |
| hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev); |
| struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1]; |
| struct sme_flush_pending flush_pending; |
| eHalStatus status; |
| int ret; |
| |
| ENTER(); |
| |
| ret = wlan_hdd_validate_context(hdd_ctx); |
| if (0 != ret) |
| return ret; |
| |
| if (VOS_FTM_MODE == hdd_get_conparam()) { |
| hddLog(LOGE, FL("Command not allowed in FTM mode")); |
| return -EINVAL; |
| } |
| if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX, data, |
| data_len, qca_wlan_vendor_peer_flush_pending_policy)) { |
| hddLog(LOGE, FL("Invalid attribute")); |
| return -EINVAL; |
| } |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR]) { |
| hddLog(LOGE, |
| FL("Attribute peerMac not provided")); |
| return -EINVAL; |
| } |
| memcpy(flush_pending.peer_addr.bytes, |
| nla_data(tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR]), |
| VOS_MAC_ADDR_SIZE); |
| |
| if (!tb[QCA_WLAN_VENDOR_ATTR_AC]) { |
| hddLog(LOGE, FL("Attribute AC not provided")); |
| return -EINVAL; |
| } |
| flush_pending.flush_ac = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_AC]); |
| hddLog(LOG1, FL("flush ac = %02x"), flush_pending.flush_ac & 0x0f); |
| |
| flush_pending.session_id = adapter->sessionId; |
| hddLog(LOG1, FL("session_id = %d"), flush_pending.session_id); |
| |
| status = sme_peer_flush_pending(hdd_ctx->hHal, &flush_pending); |
| if (!HAL_STATUS_SUCCESS(status)) { |
| hddLog(LOGE, FL("sme_config_peer_flush_pending (err=%d)"), |
| status); |
| return -EINVAL; |
| } |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * wlan_hdd_cfg80211_peer_flush_tids() - flush peer pending packets |
| * @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_peer_flush_ac() |
| * |
| * Return: 0 on success, negative errno on failure |
| */ |
| static int wlan_hdd_cfg80211_peer_flush_pending(struct wiphy *wiphy, |
| struct wireless_dev *wdev, |
| const void *data, int data_len) |
| { |
| int ret; |
| |
| vos_ssr_protect(__func__); |
| ret = __wlan_hdd_cfg80211_peer_flush_pending(wiphy, wdev, |
| data, data_len); |
| vos_ssr_unprotect(__func__); |
| |
| return ret; |
| } |
| |
| 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 |
| }, |
| |
| #ifdef WLAN_FEATURE_NAN |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_nan_request |
| }, |
| #endif |
| |
| #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 |
| }, |
| #endif |
| #ifdef FEATURE_WLAN_EXTSCAN |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_start |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_stop |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_extscan_get_valid_channels |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_extscan_get_capabilities |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_get_cached_results |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_set_bssid_hotlist |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_reset_bssid_hotlist |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_set_significant_change |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_extscan_reset_significant_change |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_LIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_epno_list |
| }, |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| |
| #ifdef WLAN_FEATURE_LINK_LAYER_STATS |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ll_stats_clear |
| }, |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ll_stats_set |
| }, |
| |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ll_stats_get |
| }, |
| #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ |
| #ifdef FEATURE_WLAN_TDLS |
| /* EXT TDLS */ |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_exttdls_enable |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_exttdls_disable |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_exttdls_get_status |
| }, |
| #endif |
| { |
| .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 |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_set_scanning_mac_oui |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_disable_dfs_chan_scan |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_get_concurrency_matrix |
| }, |
| #ifdef WLAN_FEATURE_APFIND |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_APFIND, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_apfind_cmd |
| }, |
| #endif /* WLAN_FEATURE_APFIND */ |
| |
| { |
| .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 |
| }, |
| |
| { |
| .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 |
| }, |
| #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 |
| }, |
| #endif |
| { |
| .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 |
| }, |
| { |
| .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 |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_set_ext_roam_params |
| }, |
| #ifdef FEATURE_WLAN_EXTSCAN |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_set_passpoint_list |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_RESET_PASSPOINT_LIST, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_reset_passpoint_list |
| }, |
| #endif /* FEATURE_WLAN_EXTSCAN */ |
| { |
| .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 |
| }, |
| /* OCB commands */ |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ocb_set_config |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ocb_set_utc_time |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ocb_start_timing_advert |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ocb_stop_timing_advert |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_ocb_get_tsf_timer |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_dcc_get_stats |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_dcc_clear_stats |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_dcc_update_ndl |
| }, |
| { |
| .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 | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_get_logger_supp_feature |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_wifi_logger_start |
| }, |
| #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 |
| }, |
| #endif |
| { |
| .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 |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV, |
| .doit = wlan_hdd_cfg80211_wifi_logger_get_ring_data |
| }, |
| #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 |
| }, |
| #endif |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_monitor_rssi |
| }, |
| { |
| .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 |
| }, |
| { |
| .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 |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_bpf_offload |
| }, |
| { |
| .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, |
| .doit = wlan_hdd_cfg80211_get_bus_size |
| }, |
| { |
| .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 |
| }, |
| #ifdef WLAN_FEATURE_NAN_DATAPATH |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_process_ndp_cmd |
| }, |
| #endif |
| { |
| .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 |
| }, |
| { |
| .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 |
| }, |
| #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 |
| }, |
| #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 |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_STATION, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = hdd_cfg80211_get_station_cmd |
| }, |
| { |
| .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_fast_roaming |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_txpower_scale |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = |
| QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE_DECR_DB, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_txpower_scale_decr_db |
| }, |
| { |
| .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 |
| }, |
| { |
| .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 |
| }, |
| { |
| .info.vendor_id = QCA_NL80211_VENDOR_ID, |
| .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING, |
| .flags = WIPHY_VENDOR_CMD_NEED_WDEV | |
| WIPHY_VENDOR_CMD_NEED_NETDEV | |
| WIPHY_VENDOR_CMD_NEED_RUNNING, |
| .doit = wlan_hdd_cfg80211_peer_flush_pending |
| }, |
| }; |
| |
| /* |
| * FUNCTION: wlan_hdd_cfg80211_wiphy_alloc |
| * This function is called by hdd_wlan_startup() |
| * during initialization. |
| * This function is used to allocate wiphy structure. |
| */ |
| struct wiphy *wlan_hdd_cfg80211_wiphy_alloc(int priv_size) |
| { |
| struct wiphy *wiphy; |
| ENTER(); |
| |
| /* |
| * Create wiphy device |
| */ |
| wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, priv_size); |
| |
| if (!wiphy) |
| { |
| /* Print error and jump into err label and free the memory */ |
| hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wiphy init failed", __func__); |
| return NULL; |
| } |
| |
| return wiphy; |
| } |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(4,4,0)) || \ |
| defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT) |
| /** |
| * hdd_config_sched_scan_plans_to_wiphy() - configure sched scan plans to wiphy |
| * @wiphy: pointer to wiphy |
| * @config: pointer to config |
| * |
| * Return: None |
| */ |
| static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy, |
| hdd_config_t *config) |
| { |
| wiphy->max_sched_scan_plans = MAX_SCHED_SCAN_PLANS; |
| if (config->max_sched_scan_plan_interval) |
| wiphy->max_sched_scan_plan_interval = |
| config->max_sched_scan_plan_interval; |
| if (config->max_sched_scan_plan_iterations) |
| wiphy->max_sched_scan_plan_iterations = |
| config->max_sched_scan_plan_iterations; |
| } |
| #else |
| static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy, |
| hdd_config_t *config) |
| { |
| } |
| #endif |
| |
| #ifdef CFG80211_SCAN_RANDOM_MAC_ADDR |
| 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) |
| { |
| return; |
| } |
| #endif |
| |
| /** |
| * wlan_hdd_cfg80211_add_connected_pno_support() - Set connected PNO support |
| * @wiphy: Pointer to wireless phy |
| * |
| * This function is used to set connected PNO support to kernel |
| * |
| * Return: None |
| */ |
| #if defined(CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN) |
| static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy) |
| { |
| wiphy_ext_feature_set(wiphy, |
| NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI); |
| } |
| #else |
| static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy) |
| { |
| return; |
| } |
| #endif |
| |
| #ifdef CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME |
| /** |
| * wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA frms |
| * @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) |
| { |
| return; |
| } |
| #endif |
| |
| #if defined(WLAN_FEATURE_FILS_SK) && defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) |
| 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 |
| |
| /* |
| * 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, |
| hdd_config_t *pCfg |
| ) |
| { |
| int i, j; |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| |
| ENTER(); |
| |
| /* Now bind the underlying wlan device with wiphy */ |
| set_wiphy_dev(wiphy, dev); |
| |
| wiphy->mgmt_stypes = wlan_hdd_txrx_stypes; |
| |
| |
| /* This will disable updating of NL channels from passive to |
| * active if a beacon is received on passive channel. */ |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; |
| #else |
| wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS) |
| 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 (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS) |
| wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; |
| #else |
| wiphy->country_ie_pref = NL80211_COUNTRY_IE_IGNORE_CORE; |
| #endif |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS) |
| 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 |
| |
| #if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR) |
| if (pCfg->isFastTransitionEnabled |
| #ifdef FEATURE_WLAN_LFR |
| || pCfg->isFastRoamIniFeatureEnabled |
| #endif |
| #ifdef FEATURE_WLAN_ESE |
| || pCfg->isEseIniFeatureEnabled |
| #endif |
| ) |
| { |
| wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; |
| } |
| #endif |
| #ifdef FEATURE_WLAN_TDLS |
| wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
| | WIPHY_FLAG_TDLS_EXTERNAL_SETUP; |
| #endif |
| |
| wiphy->features |= NL80211_FEATURE_HT_IBSS; |
| |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| if (pCfg->configPNOScanSupport) |
| { |
| wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; |
| wiphy->max_sched_scan_ssids = SIR_PNO_MAX_SUPP_NETWORKS; |
| wiphy->max_match_sets = SIR_PNO_MAX_SUPP_NETWORKS; |
| wiphy->max_sched_scan_ie_len = SIR_MAC_MAX_IE_LENGTH; |
| } |
| wlan_hdd_cfg80211_add_connected_pno_support(wiphy); |
| #endif/*FEATURE_WLAN_SCAN_PNO*/ |
| |
| #if defined QCA_WIFI_FTM |
| if (vos_get_conparam() != VOS_FTM_MODE) { |
| #endif |
| |
| /* even with WIPHY_FLAG_CUSTOM_REGULATORY, |
| driver can still register regulatory callback and |
| it will get regulatory settings in wiphy->band[], but |
| driver need to determine what to do with both |
| regulatory settings */ |
| |
| wiphy->reg_notifier = wlan_hdd_linux_reg_notifier; |
| |
| #if defined QCA_WIFI_FTM |
| } |
| #endif |
| |
| 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; |
| |
| /* Supports STATION & AD-HOC modes right now */ |
| wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
| | BIT(NL80211_IFTYPE_ADHOC) |
| | BIT(NL80211_IFTYPE_P2P_CLIENT) |
| | BIT(NL80211_IFTYPE_P2P_GO) |
| | BIT(NL80211_IFTYPE_AP) |
| | BIT(NL80211_IFTYPE_MONITOR); |
| |
| if( pCfg->advertiseConcurrentOperation ) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS) |
| if( pCfg->enableMCC ) { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(wlan_hdd_iface_combination); i++) { |
| if( !pCfg->allowMCCGODiffBI ) |
| 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; |
| #endif |
| } |
| |
| /* Before registering we need to update the HT capability based |
| * on ini values */ |
| if( !pCfg->ShortGI20MhzEnable ) |
| { |
| wlan_hdd_band_2_4_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; |
| wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20; |
| } |
| |
| if( !pCfg->ShortGI40MhzEnable ) |
| { |
| wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; |
| } |
| |
| if( !pCfg->nChannelBondingMode5GHz ) |
| { |
| wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; |
| } |
| |
| /* |
| * 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. |
| */ |
| wiphy->bands[IEEE80211_BAND_2GHZ] = &wlan_hdd_band_2_4_GHZ; |
| wiphy->bands[IEEE80211_BAND_2GHZ]->channels = |
| vos_mem_malloc(sizeof(hdd_channels_2_4_GHZ)); |
| if (wiphy->bands[IEEE80211_BAND_2GHZ]->channels == NULL) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Not enough memory to allocate channels")); |
| return -ENOMEM; |
| } |
| vos_mem_copy(wiphy->bands[IEEE80211_BAND_2GHZ]->channels, |
| &hdd_channels_2_4_GHZ[0], |
| sizeof(hdd_channels_2_4_GHZ)); |
| if (hdd_is_5g_supported(pHddCtx) && |
| ((eHDD_DOT11_MODE_11b != pCfg->dot11Mode) && |
| (eHDD_DOT11_MODE_11g != pCfg->dot11Mode) && |
| (eHDD_DOT11_MODE_11b_ONLY != pCfg->dot11Mode) && |
| (eHDD_DOT11_MODE_11g_ONLY != pCfg->dot11Mode))) |
| { |
| wiphy->bands[IEEE80211_BAND_5GHZ] = &wlan_hdd_band_5_GHZ; |
| wiphy->bands[IEEE80211_BAND_5GHZ]->channels = |
| vos_mem_malloc(sizeof(hdd_channels_5_GHZ)); |
| if (wiphy->bands[IEEE80211_BAND_5GHZ]->channels == NULL) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| FL("Not enough memory to allocate channels")); |
| vos_mem_free(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); |
| wiphy->bands[IEEE80211_BAND_2GHZ]->channels = NULL; |
| return -ENOMEM; |
| } |
| vos_mem_copy(wiphy->bands[IEEE80211_BAND_5GHZ]->channels, |
| &hdd_channels_5_GHZ[0], |
| sizeof(hdd_channels_5_GHZ)); |
| } |
| |
| for (i = 0; i < IEEE80211_NUM_BANDS; i++) |
| { |
| |
| if (NULL == wiphy->bands[i]) |
| continue; |
| |
| for (j = 0; j < wiphy->bands[i]->n_channels; j++) |
| { |
| struct ieee80211_supported_band *band = wiphy->bands[i]; |
| |
| if (IEEE80211_BAND_2GHZ == i && eCSR_BAND_5G == pCfg->nBandCapability) // 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 (IEEE80211_BAND_5GHZ == i && eCSR_BAND_24 == pCfg->nBandCapability) // 2G only |
| { |
| band->channels[j].flags |= IEEE80211_CHAN_DISABLED; |
| continue; |
| } |
| } |
| } |
| /*Initialise the supported cipher suite details*/ |
| wiphy->cipher_suites = hdd_cipher_suites; |
| wiphy->n_cipher_suites = ARRAY_SIZE(hdd_cipher_suites); |
| |
| /*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); |
| |
| #if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) || \ |
| defined (DFS_MASTER_OFFLOAD_IND_SUPPORT) || defined(WITH_BACKPORTS) |
| if (pCfg->enableDFSMasterCap) { |
| wiphy->flags |= WIPHY_FLAG_DFS_OFFLOAD; |
| } |
| #endif |
| |
| wiphy->max_ap_assoc_sta = pHddCtx->max_peers; |
| #ifdef QCA_HT_2040_COEX |
| wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; |
| #endif |
| |
| #ifdef CHANNEL_SWITCH_SUPPORTED |
| wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; |
| #endif |
| |
| if (pCfg->sub_20_channel_width) |
| wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; |
| |
| wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,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 |
| wlan_hdd_cfg80211_set_wiphy_fils_feature(wiphy); |
| hdd_config_sched_scan_plans_to_wiphy(wiphy, pCfg); |
| wlan_hdd_cfg80211_scan_randomization_init(wiphy); |
| wlan_hdd_cfg80211_action_frame_randomization_init(wiphy); |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /** |
| * 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 |
| * |
| * Return: void |
| */ |
| void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy) |
| { |
| int i; |
| |
| for (i = 0; i < IEEE80211_NUM_BANDS; i++) { |
| if (NULL != wiphy->bands[i] && |
| (NULL != wiphy->bands[i]->channels)) { |
| vos_mem_free(wiphy->bands[i]->channels); |
| wiphy->bands[i]->channels = NULL; |
| } |
| } |
| vos_reset_global_reg_params(); |
| } |
| |
| /* |
| * In this function, wiphy structure is updated after VOSS |
| * 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 wiphy *wiphy, |
| hdd_context_t *ctx) |
| { |
| uint32_t val32; |
| uint16_t val16; |
| tSirMacHTCapabilityInfo *ht_cap_info; |
| eHalStatus status; |
| |
| wiphy->max_ap_assoc_sta = ctx->max_peers; |
| if (!sme_IsFeatureSupportedByFW(DOT11AC)) { |
| wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap.vht_supported = 0; |
| wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap.cap = 0; |
| wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.vht_supported = 0; |
| wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap = 0; |
| } |
| |
| status = ccmCfgGetInt(ctx->hHal, WNI_CFG_HT_CAP_INFO, &val32); |
| if (eHAL_STATUS_SUCCESS != status) { |
| hddLog(VOS_TRACE_LEVEL_ERROR, |
| "%s: could not get HT capability info", |
| __func__); |
| val32 = 0; |
| } |
| val16 = (uint16_t)val32; |
| ht_cap_info = (tSirMacHTCapabilityInfo *)&val16; |
| |
| if (ht_cap_info->txSTBC == TRUE) { |
| if (NULL != wiphy->bands[IEEE80211_BAND_2GHZ]) |
| wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap |= |
| IEEE80211_HT_CAP_TX_STBC; |
| if (NULL != wiphy->bands[IEEE80211_BAND_5GHZ]) |
| wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap.cap |= |
| IEEE80211_HT_CAP_TX_STBC; |
| } |
| } |
| |
| /* In this function we are registering wiphy. */ |
| int wlan_hdd_cfg80211_register(struct wiphy *wiphy) |
| { |
| ENTER(); |
| /* Register our wiphy dev with cfg80211 */ |
| if (0 > wiphy_register(wiphy)) |
| { |
| /* print error */ |
| hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__); |
| return -EIO; |
| } |
| |
| EXIT(); |
| return 0; |
| } |
| |
| /* |
| HDD function to update wiphy capability based on target offload status. |
| |
| wlan_hdd_cfg80211_init() does initialization of all wiphy related |
| capability even before downloading firmware to the target. In discrete |
| case, host will get know certain offload capability (say sched_scan |
| caps) only after downloading firmware to the target and target boots up. |
| This function is used to override setting done in wlan_hdd_cfg80211_init() |
| based on target capability. |
| */ |
| void wlan_hdd_cfg80211_update_wiphy_caps(struct wiphy *wiphy) |
| { |
| #ifdef FEATURE_WLAN_SCAN_PNO |
| hdd_context_t *pHddCtx = wiphy_priv(wiphy); |
| hdd_config_t *pCfg = pHddCtx->cfg_ini; |
| |
| /* wlan_hdd_cfg80211_init() sets sched_scan caps already in wiphy before |
| * control comes here. Here just we need to clear it if firmware doesn't |
| * have PNO support. */ |
| if (!pCfg->PnoOffload) { |
| wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_SCHED_SCAN; |
| wiphy->max_sched_scan_ssids = 0; |
| wiphy->max_match_sets = 0; |
| wiphy->max_sched_scan_ie_len = 0; |
| } |
| #endif |
| } |
| |
| /* This function registers for all frame which supplicant is interested in */ |
| void wlan_hdd_cfg80211_register_frames(hdd_adapter_t* pAdapter) |
| { |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| /* Register for all P2P action, public action etc frames */ |
| v_U16_t type = (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_ACTION << 4); |
| |
| ENTER(); |
| /* Register frame indication call back */ |
| sme_register_mgmt_frame_ind_callback(hHal, hdd_indicate_mgmt_frame); |
| |
| /* Register for p2p ack indication */ |
| sme_register_p2p_ack_ind_callback(hHal, hdd_send_action_cnf_cb); |
| |
| /* 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_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)GAS_INITIAL_REQ, GAS_INITIAL_REQ_SIZE ); |
| |
| /* GAS Initial Response */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)GAS_INITIAL_RSP, GAS_INITIAL_RSP_SIZE ); |
| |
| /* GAS Comeback Request */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)GAS_COMEBACK_REQ, GAS_COMEBACK_REQ_SIZE ); |
| |
| /* GAS Comeback Response */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)GAS_COMEBACK_RSP, GAS_COMEBACK_RSP_SIZE ); |
| |
| /* P2P Public Action */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)P2P_PUBLIC_ACTION_FRAME, |
| P2P_PUBLIC_ACTION_FRAME_SIZE ); |
| |
| /* P2P Action */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)P2P_ACTION_FRAME, |
| P2P_ACTION_FRAME_SIZE ); |
| |
| /* WNM BSS Transition Request frame */ |
| sme_RegisterMgmtFrame(hHal, HDD_SESSION_ID_ANY, type, |
| (v_U8_t*)WNM_BSS_ACTION_FRAME, |
| WNM_BSS_ACTION_FRAME_SIZE ); |
| |
| /* WNM-Notification */ |
| sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type, |
| (v_U8_t*)WNM_NOTIFICATION_FRAME, |
| WNM_NOTIFICATION_FRAME_SIZE ); |
| } |
| |
| void wlan_hdd_cfg80211_deregister_frames(hdd_adapter_t* pAdapter) |
| { |
| tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter); |
| /* Register for all P2P action, public action etc frames */ |
| |