blob: bce5a5f29bdd45942d3ed735b87b6c2f25288f57 [file] [log] [blame]
/*
* 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,