| /* |
| * 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, 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, NULL)) { |
| 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, |
|