blob: f805735c4687d78e13f9ee8a83e3cfbe1e36563c [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)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg) +
(pHddCtx->no_of_probe_req_ouis) *
(sizeof(struct vendor_oui)));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
vos_mem_zero(pReqMsg, sizeof(*pReqMsg) +
(pHddCtx->no_of_probe_req_ouis) *
(sizeof(struct vendor_oui)));
/* Parse and fetch oui */
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) {
hddLog(LOGE, FL("attr mac oui failed"));
goto fail;
}
nla_memcpy(&pReqMsg->oui[0],
tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI],
sizeof(pReqMsg->oui));
/* populate pReqMsg for mac addr randomization */
pReqMsg->vdev_id = pAdapter->sessionId;
pReqMsg->enb_probe_req_sno_randomization = 1;
hddLog(LOG1, FL("Oui (%02x:%02x:%02x), vdev_id = %d"), pReqMsg->oui[0],
pReqMsg->oui[1], pReqMsg->oui[2], pReqMsg->vdev_id);
if (pHddCtx->cfg_ini->probe_req_ie_whitelist)
wlan_hdd_fill_whitelist_ie_attrs(&pReqMsg->ie_whitelist,
pReqMsg->probe_req_ie_bitmap,
&pReqMsg->num_vendor_oui,
(struct vendor_oui *)((uint8_t *)pReqMsg +
sizeof(*pReqMsg)),
pHddCtx);
status = sme_SetScanningMacOui(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_SetScanningMacOui failed(err=%d)"), status);
goto fail;
}
return 0;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Set the MAC address that is to be used for scanning. This is an
* SSR-protecting wrapper function.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_scanning_mac_oui(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#define MAX_CONCURRENT_MATRIX \
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX
#define MATRIX_CONFIG_PARAM_SET_SIZE_MAX \
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX
static const struct nla_policy
wlan_hdd_get_concurrency_matrix_policy[MAX_CONCURRENT_MATRIX + 1] = {
[MATRIX_CONFIG_PARAM_SET_SIZE_MAX] = {.type = NLA_U32},
};
static int
__wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
uint32_t feature_set_matrix[WLAN_HDD_MAX_FEATURE_SET] = {0};
uint8_t i, feature_sets, max_feature_sets;
struct nlattr *tb[MAX_CONCURRENT_MATRIX + 1];
struct sk_buff *reply_skb;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int ret;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (nla_parse(tb, MAX_CONCURRENT_MATRIX,
data, data_len, wlan_hdd_get_concurrency_matrix_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch max feature set */
if (!tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]) {
hddLog(LOGE, FL("Attr max feature set size failed"));
return -EINVAL;
}
max_feature_sets = nla_get_u32(tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]);
hddLog(LOG1, FL("Max feature set size: %d"), max_feature_sets);
/* Fill feature combination matrix */
feature_sets = 0;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
WIFI_FEATURE_P2P;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
WIFI_FEATURE_NAN;
/* Add more feature combinations here */
feature_sets = VOS_MIN(feature_sets, max_feature_sets);
hddLog(LOG1, FL("Number of feature sets: %d"), feature_sets);
hddLog(LOG1, "Feature set matrix");
for (i = 0; i < feature_sets; i++)
hddLog(LOG1, "[%d] 0x%02X", i, feature_set_matrix[i]);
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
sizeof(u32) * feature_sets +
NLMSG_HDRLEN);
if (reply_skb) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE,
feature_sets) ||
nla_put(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET,
sizeof(u32) * feature_sets, feature_set_matrix)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
ret = cfg80211_vendor_cmd_reply(reply_skb);
EXIT();
return ret;
}
hddLog(LOGE, FL("Feature set matrix: buffer alloc fail"));
return -ENOMEM;
}
#undef MAX_CONCURRENT_MATRIX
#undef MATRIX_CONFIG_PARAM_SET_SIZE_MAX
/**
* wlan_hdd_cfg80211_get_concurrency_matrix() - get concurrency matrix
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_concurrency_matrix(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#define MAX_ROAMING_PARAM \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX
static const struct nla_policy
wlan_hdd_set_roam_param_policy[MAX_ROAMING_PARAM + 1] = {
[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD] = {
.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD] = {
.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS] = {
.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE] = {
.type = NLA_S32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID] = {
.type = NLA_BINARY,
.len = MAC_ADDRESS_STR_LEN},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID] = {
.type = NLA_BINARY,
.len = MAC_ADDRESS_STR_LEN},
};
static int
__wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
uint8_t session_id;
struct roam_ext_params roam_params;
uint32_t cmd_type, req_id;
struct nlattr *curr_attr = NULL;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX + 1];
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX + 1];
int rem, i;
uint32_t buf_len = 0;
uint32_t count;
int ret;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
data, data_len,
wlan_hdd_set_roam_param_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch Command Type*/
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]) {
hddLog(LOGE, FL("roam cmd type failed"));
goto fail;
}
session_id = pAdapter->sessionId;
vos_mem_set(&roam_params, sizeof(roam_params),0);
cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]);
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
req_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]);
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Req Id (%d)"), req_id);
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Cmd Type (%d)"), cmd_type);
switch(cmd_type) {
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST:
i = 0;
if (tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]) {
count = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]);
} else {
hddLog(LOGE, FL("Number of networks is not provided"));
goto fail;
}
if (count &&
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST]) {
nla_for_each_nested(curr_attr,
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST],
rem) {
if (nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX,
nla_data(curr_attr), nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
goto fail;
}
/* Parse and Fetch allowed SSID list*/
if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]) {
hddLog(LOGE, FL("attr allowed ssid failed"));
goto fail;
}
buf_len = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]);
/*
* Upper Layers include a null termination character.
* Check for the actual permissible length of SSID and
* also ensure not to copy the NULL termination
* character to the driver buffer.
*/
if (buf_len && (i < MAX_SSID_ALLOWED_LIST) &&
((buf_len - 1) <= SIR_MAC_MAX_SSID_LENGTH)) {
nla_memcpy(roam_params.ssid_allowed_list[i].ssId,
tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID],
buf_len - 1);
roam_params.ssid_allowed_list[i].length =
buf_len - 1;
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("SSID[%d]: %.*s,length = %d"), i,
roam_params.ssid_allowed_list[i].length,
roam_params.ssid_allowed_list[i].ssId,
roam_params.ssid_allowed_list[i].length);
i++;
} else {
hddLog(LOGE, FL("Invalid SSID len %d,idx %d"),
buf_len, i);
}
}
}
if (i != count) {
hddLog(LOGE, FL("Invalid number of SSIDs i = %d, count = %d"),
i, count);
goto fail;
}
roam_params.num_ssid_allowed_list = i;
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Num of Allowed SSID %d"),
roam_params.num_ssid_allowed_list);
sme_update_roam_params(pHddCtx->hHal, session_id,
roam_params, REASON_ROAM_SET_SSID_ALLOWED);
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_EXTSCAN_ROAM_PARAMS:
/* Parse and fetch 5G Boost Threshold */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD]) {
hddLog(LOGE, FL("5G boost threshold failed"));
goto fail;
}
roam_params.raise_rssi_thresh_5g = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("5G Boost Threshold (%d)"),
roam_params.raise_rssi_thresh_5g);
/* Parse and fetch 5G Penalty Threshold */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD]) {
hddLog(LOGE, FL("5G penalty threshold failed"));
goto fail;
}
roam_params.drop_rssi_thresh_5g = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("5G Penalty Threshold (%d)"),
roam_params.drop_rssi_thresh_5g);
/* Parse and fetch 5G Boost Factor */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR]) {
hddLog(LOGE, FL("5G boost Factor failed"));
goto fail;
}
roam_params.raise_factor_5g = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR]);
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("5G Boost Factor (%d)"),
roam_params.raise_factor_5g);
/* Parse and fetch 5G Penalty factor */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR]) {
hddLog(LOGE, FL("5G Penalty Factor failed"));
goto fail;
}
roam_params.drop_factor_5g = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("5G Penalty factor (%d)"),
roam_params.drop_factor_5g);
/* Parse and fetch 5G Max Boost */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST]) {
hddLog(LOGE, FL("5G Max Boost failed"));
goto fail;
}
roam_params.max_raise_rssi_5g = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST]);
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("5G Max Boost (%d)"),
roam_params.max_raise_rssi_5g);
/* Parse and fetch Rssi Diff */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS]) {
hddLog(LOGE, FL("Rssi Diff failed"));
goto fail;
}
roam_params.rssi_diff = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS]);
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("RSSI Diff (%d)"),
roam_params.rssi_diff);
/* Parse and fetch Good Rssi Threshold */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER]) {
hddLog(LOGE, FL("Alert Rssi Threshold failed"));
goto fail;
}
roam_params.alert_rssi_threshold = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("Alert RSSI Threshold (%d)"),
roam_params.alert_rssi_threshold);
sme_update_roam_params(pHddCtx->hHal, session_id,
roam_params,
REASON_ROAM_EXT_SCAN_PARAMS_CHANGED);
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM:
/* Parse and fetch Activate Good Rssi Roam */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE]) {
hddLog(LOGE, FL("Activate Good Rssi Roam failed"));
goto fail;
}
roam_params.good_rssi_roam = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("Activate Good Rssi Roam (%d)"),
roam_params.good_rssi_roam);
sme_update_roam_params(pHddCtx->hHal, session_id,
roam_params, REASON_ROAM_GOOD_RSSI_CHANGED);
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS:
/* Parse and fetch number of preferred BSSID */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID]) {
hddLog(LOGE, FL("attr num of preferred bssid failed"));
goto fail;
}
count = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID]);
if (count > MAX_BSSID_FAVORED) {
hddLog(LOGE, FL("Preferred BSSID count %u exceeds max %u"),
count, MAX_BSSID_FAVORED);
goto fail;
}
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("Num of Preferred BSSID: %d"), count);
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS]) {
hddLog(LOGE, FL("attr Preferred BSSID failed"));
goto fail;
}
i = 0;
nla_for_each_nested(curr_attr,
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS],
rem) {
if (i == count) {
hddLog(LOGW, FL("Ignoring excess Preferred BSSID"));
break;
}
if (nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
nla_data(curr_attr), nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID]) {
hddLog(LOGE, FL("attr mac address failed"));
goto fail;
}
nla_memcpy(roam_params.bssid_favored[i],
tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID],
sizeof(tSirMacAddr));
hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(roam_params.bssid_favored[i]));
/* Parse and fetch preference factor*/
if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER]) {
hddLog(LOGE, FL("BSSID Preference score failed"));
goto fail;
}
roam_params.bssid_favored_factor[i] = nla_get_u32(
tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER]);
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("BSSID Preference score (%d)"),
roam_params.bssid_favored_factor[i]);
i++;
}
if (i < count)
hddLog(LOGW,
FL("Num Preferred BSSID %u less than expected %u"),
i, count);
roam_params.num_bssid_favored = i;
sme_update_roam_params(pHddCtx->hHal, session_id,
roam_params, REASON_ROAM_SET_FAVORED_BSSID);
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID:
/* Parse and fetch number of blacklist BSSID */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID]) {
hddLog(LOGE, FL("attr num of blacklist bssid failed"));
goto fail;
}
count = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID]);
if (count > MAX_BSSID_AVOID_LIST) {
hddLog(LOGE, FL("Blacklist BSSID count %u exceeds max %u"),
count, MAX_BSSID_AVOID_LIST);
goto fail;
}
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("Num of blacklist BSSID: %d"), count);
i = 0;
if (count &&
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS]) {
nla_for_each_nested(curr_attr,
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS],
rem) {
if (i == count) {
hddLog(LOGW, FL("Ignoring excess Blacklist BSSID"));
break;
}
if (nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
nla_data(curr_attr), nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID]) {
hddLog(LOGE, FL("attr blacklist addr failed"));
goto fail;
}
nla_memcpy(roam_params.bssid_avoid_list[i],
tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID],
sizeof(tSirMacAddr));
hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(
roam_params.bssid_avoid_list[i]));
i++;
}
}
if (i < count)
hddLog(LOGW,
FL("Num Blacklist BSSID %u less than expected %u"),
i, count);
roam_params.num_bssid_avoid_list = i;
sme_update_roam_params(pHddCtx->hHal, session_id,
roam_params, REASON_ROAM_SET_BLACKLIST_BSSID);
break;
}
return 0;
fail:
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_set_ext_roam_params() - set ext scan roam params
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_ext_roam_params(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_FEATURE_STATS_EXT
/**
* __wlan_hdd_cfg80211_stats_ext_request() - ext stats request
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int __wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tStatsExtRequestReq stats_ext_req;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
int ret_val;
eHalStatus status;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
stats_ext_req.request_data_len = data_len;
stats_ext_req.request_data = (void *)data;
status = sme_StatsExtRequest(pAdapter->sessionId, &stats_ext_req);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EINVAL;
return ret_val;
}
/**
* wlan_hdd_cfg80211_stats_ext_request() - ext stats request
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: int
*/
static int wlan_hdd_cfg80211_stats_ext_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_stats_ext_request(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static void wlan_hdd_cfg80211_stats_ext_callback(void* ctx, tStatsExtEvent* msg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *vendor_event;
int status;
int ret_val;
tStatsExtEvent *data = msg;
hdd_adapter_t *pAdapter = NULL;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return;
pAdapter = hdd_get_adapter_by_vdev( pHddCtx, data->vdev_id);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: vdev_id %d does not exist with host",
__func__, data->vdev_id);
return;
}
vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
NULL,
data->event_data_len +
sizeof(tANI_U32) +
NLMSG_HDRLEN + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX,
GFP_KERNEL);
if (!vendor_event)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_event_alloc failed", __func__);
return;
}
ret_val = nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_IFINDEX,
pAdapter->dev->ifindex);
if (ret_val)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_IFINDEX put fail", __func__);
kfree_skb(vendor_event);
return;
}
ret_val = nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_STATS_EXT,
data->event_data_len, data->event_data);
if (ret_val)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_STATS_EXT put fail", __func__);
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
}
/**
* wlan_hdd_cfg80211_stats_ext2_callback - stats_ext2_callback
* @ctx: hdd context
* @pmsg: stats_ext2_event
*
* Return: void
*/
static void wlan_hdd_cfg80211_stats_ext2_callback(void *ctx,
struct stats_ext2_event *pmsg)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)ctx;
int status, data_size;
struct sk_buff *vendor_event;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
if (NULL == pmsg) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"msg received here is null");
return;
}
data_size = sizeof(struct stats_ext2_event) +
(pmsg->hole_cnt)*sizeof(pmsg->hole_info_array[0]);
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL,
data_size + NLMSG_HDRLEN + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX,
GFP_KERNEL);
if (!vendor_event) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"vendor_event_alloc failed for STATS_EXT2");
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM,
pmsg->hole_cnt)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s put fail",
"QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM");
kfree_skb(vendor_event);
return;
}
if (nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO,
(pmsg->hole_cnt)*sizeof(pmsg->hole_info_array[0]),
(void *)(pmsg->hole_info_array))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s put fail",
"QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO");
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
}
void wlan_hdd_cfg80211_stats_ext_init(hdd_context_t *pHddCtx)
{
sme_StatsExtRegisterCallback(pHddCtx->hHal,
wlan_hdd_cfg80211_stats_ext_callback);
sme_register_stats_ext2_callback(pHddCtx->hHal,
wlan_hdd_cfg80211_stats_ext2_callback);
}
#endif
#ifdef FEATURE_WLAN_EXTSCAN
/*
* define short names for the global vendor params
* used by wlan_hdd_send_ext_scan_capability()
*/
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
#define PARAM_STATUS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS
#define MAX_SCAN_CACHE_SIZE \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE
#define MAX_SCAN_BUCKETS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS
#define MAX_AP_CACHE_PER_SCAN \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
#define MAX_RSSI_SAMPLE_SIZE \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
#define MAX_SCAN_RPT_THRHOLD \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
#define MAX_HOTLIST_BSSIDS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS
#define MAX_SIGNIFICANT_WIFI_CHANGE_APS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
#define MAX_BSSID_HISTORY_ENTRIES \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
#define MAX_HOTLIST_SSIDS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS
#define MAX_NUM_EPNO_NETS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS
#define MAX_NUM_EPNO_NETS_BY_SSID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
#define MAX_NUM_WHITELISTED_SSID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
#define MAX_NUM_BLACKLISTED_BSSID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID
/**
* wlan_hdd_send_ext_scan_capability - send ext scan capability to user space
* @hdd_ctx: Pointer to hdd context
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_send_ext_scan_capability(hdd_context_t *hdd_ctx)
{
int ret;
struct sk_buff *skb;
struct ext_scan_capabilities_response *data;
uint32_t nl_buf_len;
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
data = &(hdd_ctx->ext_scan_context.capability_response);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) +
(sizeof(data->status) + NLA_HDRLEN) +
(sizeof(data->max_scan_cache_size) + NLA_HDRLEN) +
(sizeof(data->max_scan_buckets) + NLA_HDRLEN) +
(sizeof(data->max_ap_cache_per_scan) + NLA_HDRLEN) +
(sizeof(data->max_rssi_sample_size) + NLA_HDRLEN) +
(sizeof(data->max_scan_reporting_threshold) + NLA_HDRLEN) +
(sizeof(data->max_hotlist_bssids) + NLA_HDRLEN) +
(sizeof(data->max_significant_wifi_change_aps) + NLA_HDRLEN) +
(sizeof(data->max_bssid_history_entries) + NLA_HDRLEN) +
(sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) +
(sizeof(data->max_number_epno_networks) + NLA_HDRLEN) +
(sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) +
(sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) +
(sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, "Req Id (%u)", data->requestId);
hddLog(LOG1, "Status (%u)", data->status);
hddLog(LOG1, "Scan cache size (%u)", data->max_scan_cache_size);
hddLog(LOG1, "Scan buckets (%u)", data->max_scan_buckets);
hddLog(LOG1, "Max AP per scan (%u)", data->max_ap_cache_per_scan);
hddLog(LOG1, "max_rssi_sample_size (%u)",
data->max_rssi_sample_size);
hddLog(LOG1, "max_scan_reporting_threshold (%u)",
data->max_scan_reporting_threshold);
hddLog(LOG1, "max_hotlist_bssids (%u)", data->max_hotlist_bssids);
hddLog(LOG1, "max_significant_wifi_change_aps (%u)",
data->max_significant_wifi_change_aps);
hddLog(LOG1, "max_bssid_history_entries (%u)",
data->max_bssid_history_entries);
hddLog(LOG1, "max_hotlist_ssids (%u)", data->max_hotlist_ssids);
hddLog(LOG1, "max_number_epno_networks (%u)",
data->max_number_epno_networks);
hddLog(LOG1, "max_number_epno_networks_by_ssid (%u)",
data->max_number_epno_networks_by_ssid);
hddLog(LOG1, "max_number_of_white_listed_ssid (%u)",
data->max_number_of_white_listed_ssid);
hddLog(LOG1, "max_number_of_black_listed_bssid (%u)",
data->max_number_of_black_listed_bssid);
if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) ||
nla_put_u32(skb, PARAM_STATUS, data->status) ||
nla_put_u32(skb, MAX_SCAN_CACHE_SIZE, data->max_scan_cache_size) ||
nla_put_u32(skb, MAX_SCAN_BUCKETS, data->max_scan_buckets) ||
nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN,
data->max_ap_cache_per_scan) ||
nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE,
data->max_rssi_sample_size) ||
nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD,
data->max_scan_reporting_threshold) ||
nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->max_hotlist_bssids) ||
nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS,
data->max_significant_wifi_change_aps) ||
nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES,
data->max_bssid_history_entries) ||
nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->max_hotlist_ssids) ||
nla_put_u32(skb, MAX_NUM_EPNO_NETS,
data->max_number_epno_networks) ||
nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID,
data->max_number_epno_networks_by_ssid) ||
nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID,
data->max_number_of_white_listed_ssid) ||
nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID,
data->max_number_of_black_listed_bssid)) {
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_send_ext_scan_capability()
*/
#undef PARAM_REQUEST_ID
#undef PARAM_STATUS
#undef MAX_SCAN_CACHE_SIZE
#undef MAX_SCAN_BUCKETS
#undef MAX_AP_CACHE_PER_SCAN
#undef MAX_RSSI_SAMPLE_SIZE
#undef MAX_SCAN_RPT_THRHOLD
#undef MAX_HOTLIST_BSSIDS
#undef MAX_SIGNIFICANT_WIFI_CHANGE_APS
#undef MAX_BSSID_HISTORY_ENTRIES
#undef MAX_HOTLIST_SSIDS
#undef MAX_NUM_EPNO_NETS
#undef MAX_NUM_EPNO_NETS_BY_SSID
#undef MAX_NUM_WHITELISTED_SSID
#undef MAX_NUM_BLACKLISTED_BSSID
static int __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
unsigned long rc;
struct hdd_ext_scan_context *context;
tpSirGetExtScanCapabilitiesReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
eHalStatus status;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id %d"), pReqMsg->requestId);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id %d"), pReqMsg->sessionId);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
context->request_id = pReqMsg->requestId;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
status = sme_ExtScanGetCapabilities(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ExtScanGetCapabilities failed(err=%d)"), status);
goto fail;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
return -ETIMEDOUT;
}
ret = wlan_hdd_send_ext_scan_capability(pHddCtx);
if (ret)
hddLog(LOGE, FL("Failed to send ext scan capability to user space"));
EXIT();
return ret;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_get_capabilities() - get ext scan capabilities
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_get_cached_results()
*/
#define PARAM_MAX \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
#define PARAM_FLUSH \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH
/**
* __wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* Each WMI event with cached scan results data chunk results in
* function call wlan_hdd_cfg80211_extscan_cached_results_ind and each
* data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb.
*
* If timeout happens before receiving all of the data, this function sets
* a context variable @ignore_cached_results to %true, all of the next data
* chunks are checked against this variable and dropped.
*
* Return: 0 on success; error number otherwise.
*/
static int __wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanGetCachedResultsReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct hdd_ext_scan_context *context;
struct nlattr *tb[PARAM_MAX + 1];
eHalStatus status;
int retval = 0;
unsigned long rc;
/* ENTER() intentionally not used in a frequently invoked API */
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]);
pReqMsg->sessionId = pAdapter->sessionId;
/* Parse and fetch flush parameter */
if (!tb[PARAM_FLUSH]) {
hddLog(LOGE, FL("attr flush failed"));
goto fail;
}
pReqMsg->flush = nla_get_u8(tb[PARAM_FLUSH]);
hddLog(LOG1, FL("Req Id: %u Session Id: %d Flush: %d"),
pReqMsg->requestId, pReqMsg->sessionId, pReqMsg->flush);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
context->request_id = pReqMsg->requestId;
context->ignore_cached_results = false;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
status = sme_getCachedResults(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_getCachedResults failed(err=%d)"), status);
goto fail;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
retval = -ETIMEDOUT;
spin_lock(&hdd_context_lock);
context->ignore_cached_results = true;
spin_unlock(&hdd_context_lock);
} else {
spin_lock(&hdd_context_lock);
retval = context->response_status;
spin_unlock(&hdd_context_lock);
}
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_get_cached_results()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_FLUSH
/**
* wlan_hdd_cfg80211_extscan_get_cached_results() - extscan get cached results
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* Each WMI event with cached scan results data chunk results in
* function call wlan_hdd_cfg80211_extscan_cached_results_ind and each
* data chunk is sent up the layer in cfg80211_vendor_cmd_alloc_reply_skb.
*
* If timeout happens before receiving all of the data, this function sets
* a context variable @ignore_cached_results to %true, all of the next data
* chunks are checked against this variable and dropped.
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanSetBssidHotListReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *apTh;
struct hdd_ext_scan_context *context;
uint32_t request_id;
eHalStatus status;
tANI_U8 i;
int rem, retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(LOG1, FL("Req Id %d"), pReqMsg->requestId);
/* Parse and fetch number of APs */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]) {
hddLog(LOGE, FL("attr number of AP failed"));
goto fail;
}
pReqMsg->numAp = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]);
if (pReqMsg->numAp > WLAN_EXTSCAN_MAX_HOTLIST_APS) {
hddLog(LOGE, FL("Number of AP: %u exceeds max: %u"),
pReqMsg->numAp, WLAN_EXTSCAN_MAX_HOTLIST_APS);
goto fail;
}
hddLog(LOG1, FL("Number of AP %d"), pReqMsg->numAp);
/* Parse and fetch lost ap sample size */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]) {
hddLog(LOGE, FL("attr lost ap sample size failed"));
goto fail;
}
pReqMsg->lost_ap_sample_size = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]);
hddLog(LOG1, FL("Lost ap sample size %d"), pReqMsg->lost_ap_sample_size);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(LOG1, FL("Session Id %d"), pReqMsg->sessionId);
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM]) {
hddLog(LOGE, FL("attr ap threshold failed"));
goto fail;
}
i = 0;
nla_for_each_nested(apTh,
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM], rem) {
if (i == pReqMsg->numAp) {
hddLog(LOGW, FL("Ignoring excess AP"));
break;
}
if (nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(apTh), nla_len(apTh),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]) {
hddLog(LOGE, FL("attr mac address failed"));
goto fail;
}
nla_memcpy(pReqMsg->ap[i].bssid,
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID],
sizeof(tSirMacAddr));
hddLog(LOG1, MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pReqMsg->ap[i].bssid));
/* Parse and fetch low RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]) {
hddLog(LOGE, FL("attr low RSSI failed"));
goto fail;
}
pReqMsg->ap[i].low = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]);
hddLog(LOG1, FL("RSSI low %d"), pReqMsg->ap[i].low);
/* Parse and fetch high RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]) {
hddLog(LOGE, FL("attr high RSSI failed"));
goto fail;
}
pReqMsg->ap[i].high = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]);
hddLog(LOG1, FL("RSSI High %d"), pReqMsg->ap[i].high);
i++;
}
if (i < pReqMsg->numAp) {
hddLog(LOGW, FL("Number of AP %u less than expected %u"),
i, pReqMsg->numAp);
pReqMsg->numAp = i;
}
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_SetBssHotlist(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_SetBssHotlist failed(err=%d)"), status);
goto fail;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_SetBssHotlist timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_set_bssid_hotlist() - set ext scan bssid hotlist
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_set_significant_change(
struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanSetSigChangeReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *apTh;
struct hdd_ext_scan_context *context;
uint32_t request_id;
eHalStatus status;
tANI_U8 i;
int rem;
int retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(LOG1, FL("Req Id %d"), pReqMsg->requestId);
/* Parse and fetch RSSI sample size */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE])
{
hddLog(LOGE, FL("attr RSSI sample size failed"));
goto fail;
}
pReqMsg->rssiSampleSize = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE]);
hddLog(LOG1, FL("RSSI sample size %u"), pReqMsg->rssiSampleSize);
/* Parse and fetch lost AP sample size */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE])
{
hddLog(LOGE, FL("attr lost AP sample size failed"));
goto fail;
}
pReqMsg->lostApSampleSize = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE]);
hddLog(LOG1, FL("Lost AP sample size %u"), pReqMsg->lostApSampleSize);
/* Parse and fetch AP min breaching */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING])
{
hddLog(LOGE, FL("attr AP min breaching"));
goto fail;
}
pReqMsg->minBreaching = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING]);
hddLog(LOG1, FL("AP min breaching %u"), pReqMsg->minBreaching);
/* Parse and fetch number of APs */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP]) {
hddLog(LOGE, FL("attr number of AP failed"));
goto fail;
}
pReqMsg->numAp = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP]);
if (pReqMsg->numAp > WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS) {
hddLog(LOGE, FL("Number of AP %u exceeds max %u"),
pReqMsg->numAp, WLAN_EXTSCAN_MAX_SIGNIFICANT_CHANGE_APS);
goto fail;
}
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(LOG1, FL("Number of AP %d Session Id %d"), pReqMsg->numAp,
pReqMsg->sessionId);
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM]) {
hddLog(LOGE, FL("attr ap threshold failed"));
goto fail;
}
i = 0;
nla_for_each_nested(apTh,
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM], rem) {
if (i == pReqMsg->numAp) {
hddLog(LOGW, FL("Ignoring excess AP"));
break;
}
if (nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(apTh), nla_len(apTh),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]) {
hddLog(LOGE, FL("attr mac address failed"));
goto fail;
}
nla_memcpy(pReqMsg->ap[i].bssid,
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID],
sizeof(tSirMacAddr));
hddLog(LOG1, MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pReqMsg->ap[i].bssid));
/* Parse and fetch low RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]) {
hddLog(LOGE, FL("attr low RSSI failed"));
goto fail;
}
pReqMsg->ap[i].low = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]);
hddLog(LOG1, FL("RSSI low %d"), pReqMsg->ap[i].low);
/* Parse and fetch high RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]) {
hddLog(LOGE, FL("attr high RSSI failed"));
goto fail;
}
pReqMsg->ap[i].high = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]);
hddLog(LOG1, FL("RSSI High %d"), pReqMsg->ap[i].high);
i++;
}
if (i < pReqMsg->numAp) {
hddLog(LOGW, FL("Number of AP %u less than expected %u"),
i, pReqMsg->numAp);
pReqMsg->numAp = i;
}
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_SetSignificantChange(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_SetSignificantChange failed(err=%d)"), status);
goto fail;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_SetSignificantChange timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_set_significant_change() - set significant change
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_extscan_set_significant_change(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_set_significant_change(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
uint32_t chan_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
uint8_t num_channels = 0, num_chan_new = 0, buf[256] = {0};
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
tANI_U32 requestId, maxChannels;
tWifiBand wifiBand;
eHalStatus status;
struct sk_buff *reply_skb;
int i, j, k, retval, len = 0;
/* ENTER() intentionally not used in a frequently invoked API */
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
return -EINVAL;
}
requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
/* Parse and fetch wifi band */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]) {
hddLog(LOGE, FL("attr wifi band failed"));
return -EINVAL;
}
wifiBand = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]);
/* Parse and fetch max channels */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]) {
hddLog(LOGE, FL("attr max channels failed"));
return -EINVAL;
}
maxChannels = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]);
hddLog(LOG1, FL("Req Id: %d Wifi band: %d Max channels: %d"),
requestId, wifiBand, maxChannels);
status = sme_GetValidChannelsByBand((tHalHandle)(pHddCtx->hHal),
wifiBand, chan_list,
&num_channels);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(LOGE,
FL("sme_GetValidChannelsByBand failed (err=%d)"), status);
return -EINVAL;
}
num_channels = VOS_MIN(num_channels, maxChannels);
/* remove the DSRC channels from the list */
num_chan_new = 0;
for (i = 0; i < num_channels; i++) {
if (!vos_is_dsrc_channel(chan_list[i])) {
chan_list[num_chan_new] = chan_list[i];
num_chan_new++;
}
}
num_channels = num_chan_new;
/* remove the indoor only channels if iface is SAP */
if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
!strncmp(hdd_get_fwpath(), "ap", 2)) {
num_chan_new = 0;
for (i = 0; i < num_channels; i++)
for (j = 0; j < IEEE80211_NUM_BANDS; j++) {
if (wiphy->bands[j] == NULL)
continue;
for (k = 0; k < wiphy->bands[j]->n_channels; k++) {
if ((chan_list[i] ==
wiphy->bands[j]->channels[k].center_freq) &&
(!(wiphy->bands[j]->channels[k].flags &
IEEE80211_CHAN_INDOOR_ONLY))) {
chan_list[num_chan_new] = chan_list[i];
num_chan_new++;
}
}
}
}
hddLog(LOG1, FL("Number of channels: %d"), num_chan_new);
for (i = 0; i < num_chan_new; i++)
len += scnprintf(buf + len, sizeof(buf) - len, "%u ", chan_list[i]);
hddLog(LOG1, "Channels: %s", buf);
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
sizeof(u32) * num_chan_new +
NLMSG_HDRLEN);
if (reply_skb) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS,
num_chan_new) ||
nla_put(reply_skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS,
sizeof(u32) * num_chan_new, chan_list)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
retval = cfg80211_vendor_cmd_reply(reply_skb);
return retval;
}
hddLog(LOGE, FL("valid channels: buffer alloc fail"));
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_get_valid_channels() - get ext scan valid channels
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_extscan_update_dwell_time_limits() - update dwell times
* @req_msg: Pointer to request message
* @bkt_idx: Index of current bucket being processed
* @active_min: minimum active dwell time
* @active_max: maximum active dwell time
* @passive_min: minimum passive dwell time
* @passive_max: maximum passive dwell time
*
* Return: none
*/
static void hdd_extscan_update_dwell_time_limits(
tpSirWifiScanCmdReqParams req_msg, uint32_t bkt_idx,
uint32_t active_min, uint32_t active_max,
uint32_t passive_min, uint32_t passive_max)
{
/* update per-bucket dwell times */
if (req_msg->buckets[bkt_idx].min_dwell_time_active >
active_min) {
req_msg->buckets[bkt_idx].min_dwell_time_active =
active_min;
}
if (req_msg->buckets[bkt_idx].max_dwell_time_active <
active_max) {
req_msg->buckets[bkt_idx].max_dwell_time_active =
active_max;
}
if (req_msg->buckets[bkt_idx].min_dwell_time_passive >
passive_min) {
req_msg->buckets[bkt_idx].min_dwell_time_passive =
passive_min;
}
if (req_msg->buckets[bkt_idx].max_dwell_time_passive <
passive_max) {
req_msg->buckets[bkt_idx].max_dwell_time_passive =
passive_max;
}
/* update dwell-time across all buckets */
if (req_msg->min_dwell_time_active >
req_msg->buckets[bkt_idx].min_dwell_time_active) {
req_msg->min_dwell_time_active =
req_msg->buckets[bkt_idx].min_dwell_time_active;
}
if (req_msg->max_dwell_time_active <
req_msg->buckets[bkt_idx].max_dwell_time_active) {
req_msg->max_dwell_time_active =
req_msg->buckets[bkt_idx].max_dwell_time_active;
}
if (req_msg->min_dwell_time_passive >
req_msg->buckets[bkt_idx].min_dwell_time_passive) {
req_msg->min_dwell_time_passive =
req_msg->buckets[bkt_idx].min_dwell_time_passive;
}
if (req_msg->max_dwell_time_passive >
req_msg->buckets[bkt_idx].max_dwell_time_passive) {
req_msg->max_dwell_time_passive =
req_msg->buckets[bkt_idx].max_dwell_time_passive;
}
}
/**
* hdd_extscan_channel_max_reached() - channel max reached
* @req: extscan request structure
* @total_channels: total number of channels
*
* Return: true if total channels reached max, false otherwise
*/
static bool hdd_extscan_channel_max_reached(tSirWifiScanCmdReqParams *req,
uint8_t total_channels)
{
if (total_channels == WLAN_EXTSCAN_MAX_CHANNELS) {
hddLog(LOGW,
FL("max #of channels %d reached, taking only first %d bucket(s)"),
total_channels, req->numBuckets);
return true;
}
return false;
}
static int hdd_extscan_start_fill_bucket_channel_spec(
hdd_context_t *pHddCtx,
tpSirWifiScanCmdReqParams pReqMsg,
struct nlattr **tb)
{
struct nlattr *bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *buckets;
struct nlattr *channels;
int rem1, rem2;
eHalStatus status;
uint8_t bktIndex, j, numChannels, total_channels = 0;
uint32_t expected_buckets;
uint32_t chanList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
uint32_t min_dwell_time_active_bucket =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
uint32_t max_dwell_time_active_bucket =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
uint32_t min_dwell_time_passive_bucket =
pHddCtx->cfg_ini->extscan_passive_max_chn_time;
uint32_t max_dwell_time_passive_bucket =
pHddCtx->cfg_ini->extscan_passive_max_chn_time;
pReqMsg->min_dwell_time_active =
pReqMsg->max_dwell_time_active =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
pReqMsg->min_dwell_time_passive =
pReqMsg->max_dwell_time_passive =
pHddCtx->cfg_ini->extscan_passive_max_chn_time;
expected_buckets = pReqMsg->numBuckets;
pReqMsg->numBuckets = 0;
bktIndex = 0;
nla_for_each_nested(buckets,
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC], rem1) {
if (bktIndex >= expected_buckets) {
hddLog(LOGW, FL("ignoring excess buckets"));
break;
}
if (nla_parse(bucket,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(buckets), nla_len(buckets),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch bucket spec */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]) {
hddLog(LOGE, FL("attr bucket index failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].bucket = nla_get_u8(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]);
/* Parse and fetch wifi band */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]) {
hddLog(LOGE, FL("attr wifi band failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].band = nla_get_u8(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]);
/* Parse and fetch period */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]) {
hddLog(LOGE, FL("attr period failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].period = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]);
/* Parse and fetch report events */
if (!bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]) {
hddLog(LOGE, FL("attr report events failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].reportEvents = nla_get_u8(
bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]);
/* Parse and fetch max period */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]) {
hddLog(LOGE, FL("attr max period failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].max_period = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]);
/* Parse and fetch base */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]) {
hddLog(LOGE, FL("attr base failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].exponent = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BASE]);
/* Parse and fetch step count */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]) {
hddLog(LOGE, FL("attr step count failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].step_count = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]);
hddLog(LOG1, FL("Bucket spec Index: %d Wifi band: %d period: %d report events: %d max period: %u base: %u Step count: %u"),
pReqMsg->buckets[bktIndex].bucket,
pReqMsg->buckets[bktIndex].band,
pReqMsg->buckets[bktIndex].period,
pReqMsg->buckets[bktIndex].reportEvents,
pReqMsg->buckets[bktIndex].max_period,
pReqMsg->buckets[bktIndex].exponent,
pReqMsg->buckets[bktIndex].step_count);
/* start with known good values for bucket dwell times */
pReqMsg->buckets[bktIndex].min_dwell_time_active =
pReqMsg->buckets[bktIndex].max_dwell_time_active =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
pReqMsg->buckets[bktIndex].min_dwell_time_passive =
pReqMsg->buckets[bktIndex].max_dwell_time_passive =
pHddCtx->cfg_ini->extscan_passive_max_chn_time;
/* Framework shall pass the channel list if the input WiFi band is
* WIFI_BAND_UNSPECIFIED.
* If the input WiFi band is specified (any value other than
* WIFI_BAND_UNSPECIFIED) then driver populates the channel list
*/
if (pReqMsg->buckets[bktIndex].band != WIFI_BAND_UNSPECIFIED) {
if (hdd_extscan_channel_max_reached(pReqMsg,
total_channels))
return 0;
numChannels = 0;
hddLog(LOG1, "WiFi band is specified, driver to fill channel list");
status = sme_GetValidChannelsByBand(pHddCtx->hHal,
pReqMsg->buckets[bktIndex].band,
chanList, &numChannels);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_GetValidChannelsByBand failed (err=%d)"),
status);
return -EINVAL;
}
hddLog(LOG1, FL("before trimming, num_channels: %d"),
numChannels);
pReqMsg->buckets[bktIndex].numChannels =
VOS_MIN(numChannels,
(WLAN_EXTSCAN_MAX_CHANNELS - total_channels));
hddLog(LOG1,
FL("Adj Num channels/bucket: %d total_channels: %d"),
pReqMsg->buckets[bktIndex].numChannels,
total_channels);
total_channels += pReqMsg->buckets[bktIndex].numChannels;
for (j = 0; j < pReqMsg->buckets[bktIndex].numChannels;
j++) {
pReqMsg->buckets[bktIndex].channels[j].channel =
chanList[j];
pReqMsg->buckets[bktIndex].channels[j].
chnlClass = 0;
if (CSR_IS_CHANNEL_DFS(
vos_freq_to_chan(chanList[j]))) {
pReqMsg->buckets[bktIndex].channels[j].
passive = 1;
pReqMsg->buckets[bktIndex].channels[j].
dwellTimeMs =
pHddCtx->cfg_ini->
extscan_passive_max_chn_time;
/* reconfigure per-bucket dwell time */
if (min_dwell_time_passive_bucket >
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
min_dwell_time_passive_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
if (max_dwell_time_passive_bucket <
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
max_dwell_time_passive_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
} else {
pReqMsg->buckets[bktIndex].channels[j].
passive = 0;
pReqMsg->buckets[bktIndex].channels[j].
dwellTimeMs =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
/* reconfigure per-bucket dwell times */
if (min_dwell_time_active_bucket >
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
min_dwell_time_active_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
if (max_dwell_time_active_bucket <
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
max_dwell_time_active_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
}
hddLog(LOG1,
"Channel %u Passive %u Dwell time %u ms Class %u",
pReqMsg->buckets[bktIndex].channels[j].channel,
pReqMsg->buckets[bktIndex].channels[j].passive,
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs,
pReqMsg->buckets[bktIndex].channels[j].chnlClass);
}
hdd_extscan_update_dwell_time_limits(
pReqMsg, bktIndex,
min_dwell_time_active_bucket,
max_dwell_time_active_bucket,
min_dwell_time_passive_bucket,
max_dwell_time_passive_bucket);
hddLog(LOG1, FL("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d"),
bktIndex,
pReqMsg->buckets[bktIndex].min_dwell_time_active,
pReqMsg->buckets[bktIndex].max_dwell_time_active,
pReqMsg->buckets[bktIndex].min_dwell_time_passive,
pReqMsg->buckets[bktIndex].max_dwell_time_passive);
bktIndex++;
pReqMsg->numBuckets++;
continue;
}
/* Parse and fetch number of channels */
if (!bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]) {
hddLog(LOGE, FL("attr num channels failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].numChannels =
nla_get_u32(bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]);
hddLog(LOG1, FL("before trimming: num channels %d"),
pReqMsg->buckets[bktIndex].numChannels);
pReqMsg->buckets[bktIndex].numChannels =
VOS_MIN(pReqMsg->buckets[bktIndex].numChannels,
(WLAN_EXTSCAN_MAX_CHANNELS - total_channels));
hddLog(LOG1,
FL("Num channels/bucket: %d total_channels: %d"),
pReqMsg->buckets[bktIndex].numChannels,
total_channels);
if (hdd_extscan_channel_max_reached(pReqMsg, total_channels))
return 0;
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC]) {
hddLog(LOGE, FL("attr channel spec failed"));
return -EINVAL;
}
j = 0;
nla_for_each_nested(channels,
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC], rem2) {
if ((j >= pReqMsg->buckets[bktIndex].numChannels) ||
hdd_extscan_channel_max_reached(pReqMsg,
total_channels))
break;
if (nla_parse(channel,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(channels), nla_len(channels),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch channel */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) {
hddLog(LOGE, FL("attr channel failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].channel =
nla_get_u32(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]);
hddLog(LOG1, FL("channel %u"),
pReqMsg->buckets[bktIndex].channels[j].channel);
/* Parse and fetch dwell time */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) {
hddLog(LOGE, FL("attr dwelltime failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs =
nla_get_u32(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]);
/* Override dwell time if required */
if (pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs <
pHddCtx->cfg_ini->extscan_active_min_chn_time ||
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs >
pHddCtx->cfg_ini->extscan_active_max_chn_time) {
hddLog(LOG1,
FL("WiFi band is unspecified, dwellTime:%d"),
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs);
if (CSR_IS_CHANNEL_DFS(
vos_freq_to_chan(
pReqMsg->buckets[bktIndex].channels[j].channel))) {
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs =
pHddCtx->cfg_ini->extscan_passive_max_chn_time;
} else {
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs =
pHddCtx->cfg_ini->extscan_active_max_chn_time;
}
}
hddLog(LOG1, FL("New Dwell time (%u ms)"),
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs);
if (CSR_IS_CHANNEL_DFS(
vos_freq_to_chan(
pReqMsg->buckets[bktIndex].channels[j].channel))) {
if(min_dwell_time_passive_bucket >
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
min_dwell_time_passive_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
if(max_dwell_time_passive_bucket <
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
max_dwell_time_passive_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
} else {
if(min_dwell_time_active_bucket >
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
min_dwell_time_active_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
if(max_dwell_time_active_bucket <
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs) {
max_dwell_time_active_bucket =
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs;
}
}
/* Parse and fetch channel spec passive */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) {
hddLog(LOGE,
FL("attr channel spec passive failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].passive =
nla_get_u8(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]);
hddLog(LOG1, FL("Chnl spec passive %u"),
pReqMsg->buckets[bktIndex].channels[j].passive);
/* Override scan type if required */
if (CSR_IS_CHANNEL_DFS(
vos_freq_to_chan(
pReqMsg->buckets[bktIndex].channels[j].channel))) {
pReqMsg->buckets[bktIndex].channels[j].passive = TRUE;
} else {
pReqMsg->buckets[bktIndex].channels[j].passive = FALSE;
}
j++;
total_channels++;
}
hdd_extscan_update_dwell_time_limits(
pReqMsg, bktIndex,
min_dwell_time_active_bucket,
max_dwell_time_active_bucket,
min_dwell_time_passive_bucket,
max_dwell_time_passive_bucket);
hddLog(LOG1, FL("bktIndex:%d actv_min:%d actv_max:%d pass_min:%d pass_max:%d"),
bktIndex,
pReqMsg->buckets[bktIndex].min_dwell_time_active,
pReqMsg->buckets[bktIndex].max_dwell_time_active,
pReqMsg->buckets[bktIndex].min_dwell_time_passive,
pReqMsg->buckets[bktIndex].max_dwell_time_passive);
bktIndex++;
pReqMsg->numBuckets++;
}
hddLog(LOG1, FL("Global: actv_min:%d actv_max:%d pass_min:%d pass_max:%d"),
pReqMsg->min_dwell_time_active,
pReqMsg->max_dwell_time_active,
pReqMsg->min_dwell_time_passive,
pReqMsg->max_dwell_time_passive);
return 0;
}
/*
* hdd_extscan_map_usr_drv_config_flags() - map userspace to driver config flags
* @config_flags - [input] configuration flags.
*
* This function maps user space received configuration flags to
* driver representation.
*
* Return: configuration flags
*/
static uint32_t hdd_extscan_map_usr_drv_config_flags(uint32_t config_flags)
{
uint32_t configuration_flags = 0;
if (config_flags & EXTSCAN_LP_EXTENDED_BATCHING)
configuration_flags |= EXTSCAN_LP_EXTENDED_BATCHING;
return configuration_flags;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_start()
*/
#define PARAM_MAX \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
#define PARAM_BASE_PERIOD \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD
#define PARAM_MAX_AP_PER_SCAN \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN
#define PARAM_RPT_THRHLD_PERCENT \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
#define PARAM_RPT_THRHLD_NUM_SCANS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
#define PARAM_NUM_BUCKETS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
#define PARAM_CONFIG_FLAGS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS
/**
* __wlan_hdd_cfg80211_extscan_start() - start extscan
* @wiphy: Pointer to wireless phy.
* @wdev: Pointer to wireless device.
* @data: Pointer to input data.
* @data_len: Length of @data.
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirWifiScanCmdReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
struct hdd_ext_scan_context *context;
uint32_t request_id, num_buckets;
eHalStatus status;
int retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]);
pReqMsg->sessionId = pAdapter->sessionId;
/* Parse and fetch base period */
if (!tb[PARAM_BASE_PERIOD]) {
hddLog(LOGE, FL("attr base period failed"));
goto fail;
}
pReqMsg->basePeriod = nla_get_u32(tb[PARAM_BASE_PERIOD]);
/* Parse and fetch max AP per scan */
if (!tb[PARAM_MAX_AP_PER_SCAN]) {
hddLog(LOGE, FL("attr max_ap_per_scan failed"));
goto fail;
}
pReqMsg->maxAPperScan = nla_get_u32(tb[PARAM_MAX_AP_PER_SCAN]);
/* Parse and fetch report threshold percent */
if (!tb[PARAM_RPT_THRHLD_PERCENT]) {
hddLog(LOGE, FL("attr report_threshold percent failed"));
goto fail;
}
pReqMsg->report_threshold_percent = nla_get_u8(
tb[PARAM_RPT_THRHLD_PERCENT]);
/* Parse and fetch report threshold num scans */
if (!tb[PARAM_RPT_THRHLD_NUM_SCANS]) {
hddLog(LOGE, FL("attr report_threshold num scans failed"));
goto fail;
}
pReqMsg->report_threshold_num_scans = nla_get_u8(
tb[PARAM_RPT_THRHLD_NUM_SCANS]);
hddLog(LOG1, FL("Req Id: %d Session Id: %d Base Period: %d Max AP per Scan: %d Rpt Thld percent: %d Rpt Thld num scans: %d"),
pReqMsg->requestId, pReqMsg->sessionId,
pReqMsg->basePeriod, pReqMsg->maxAPperScan,
pReqMsg->report_threshold_percent,
pReqMsg->report_threshold_num_scans);
/* Parse and fetch number of buckets */
if (!tb[PARAM_NUM_BUCKETS]) {
hddLog(LOGE, FL("attr number of buckets failed"));
goto fail;
}
num_buckets = nla_get_u8(tb[PARAM_NUM_BUCKETS]);
if (num_buckets > WLAN_EXTSCAN_MAX_BUCKETS) {
hddLog(LOGW,
FL("Exceeded MAX number of buckets: %d"),
WLAN_EXTSCAN_MAX_BUCKETS);
num_buckets = WLAN_EXTSCAN_MAX_BUCKETS;
}
hddLog(LOG1, FL("Input: Number of Buckets %d"), num_buckets);
pReqMsg->numBuckets = num_buckets;
/* This is optional attribute, if not present set it to 0 */
if (!tb[PARAM_CONFIG_FLAGS])
pReqMsg->configuration_flags = 0;
else
pReqMsg->configuration_flags =
hdd_extscan_map_usr_drv_config_flags(
nla_get_u32(tb[PARAM_CONFIG_FLAGS]));
hddLog(LOG1, FL("Configuration flags: %u"),
pReqMsg->configuration_flags);
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC]) {
hddLog(LOGE, FL("attr bucket spec failed"));
goto fail;
}
if (hdd_extscan_start_fill_bucket_channel_spec(pHddCtx, pReqMsg, tb))
goto fail;
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
context->buckets_scanned = 0;
spin_unlock(&hdd_context_lock);
status = sme_ExtScanStart(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_ExtScanStart failed(err=%d)"), status);
goto fail;
}
pHddCtx->ext_scan_start_since_boot = vos_get_monotonic_boottime();
hddLog(LOG1, FL("Timestamp since boot: %llu"),
pHddCtx->ext_scan_start_since_boot);
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ExtScanStart timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_start()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_BASE_PERIOD
#undef PARAMS_MAX_AP_PER_SCAN
#undef PARAMS_RPT_THRHLD_PERCENT
#undef PARAMS_RPT_THRHLD_NUM_SCANS
#undef PARAMS_NUM_BUCKETS
#undef PARAM_CONFIG_FLAGS
/**
* wlan_hdd_cfg80211_extscan_start() - start extscan
* @wiphy: Pointer to wireless phy.
* @wdev: Pointer to wireless device.
* @data: Pointer to input data.
* @data_len: Length of @data.
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_stop()
*/
#define PARAM_MAX \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
/**
* wlan_hdd_cfg80211_extscan_stop() - stop extscan
* @wiphy: Pointer to wireless phy.
* @wdev: Pointer to wireless device.
* @data: Pointer to input data.
* @data_len: Length of @data.
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanStopReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct hdd_ext_scan_context *context;
struct nlattr *tb[PARAM_MAX + 1];
uint32_t request_id;
eHalStatus status;
int retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(LOG1, FL("Req Id %d Session Id %d"),
pReqMsg->requestId, pReqMsg->sessionId);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_ExtScanStop(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_ExtScanStop failed(err=%d)"), status);
goto fail;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ExtScanStop timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_stop() - stop extscan
* @wiphy: Pointer to wireless phy.
* @wdev: Pointer to wireless device.
* @data: Pointer to input data.
* @data_len: Length of @data.
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_stop()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
static int __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanResetBssidHotlistReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct hdd_ext_scan_context *context;
uint32_t request_id;
eHalStatus status;
int retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(LOG1, FL("Req Id %d Session Id %d"),
pReqMsg->requestId, pReqMsg->sessionId);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_ResetBssHotlist(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_ResetBssHotlist failed(err=%d)"), status);
goto fail;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ResetBssHotlist timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_reset_bssid_hotlist() - reset bssid hot list
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_reset_significant_change(
struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
tpSirExtScanResetSignificantChangeReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct hdd_ext_scan_context *context;
uint32_t request_id;
eHalStatus status;
int retval;
unsigned long rc;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
retval = wlan_hdd_validate_context(pHddCtx);
if (0 != retval)
return -EINVAL;
if (!pHddCtx->cfg_ini->extscan_enabled) {
hddLog(LOGE, FL("extscan not supported"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(LOG1, FL("Req Id %d Session Id %d"),
pReqMsg->requestId, pReqMsg->sessionId);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_ResetSignificantChange(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_ResetSignificantChange failed(err=%d)"), status);
goto fail;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ResetSignificantChange timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_extscan_reset_significant_change() - reset significant
* change
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static
int wlan_hdd_cfg80211_extscan_reset_significant_change(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_reset_significant_change(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_extscan_epno_fill_network_list() - epno fill network list
* @hddctx: HDD context
* @req_msg: request message
* @tb: vendor attribute table
*
* This function reads the network block NL vendor attributes from %tb and
* fill in the epno request message.
*
* Return: 0 on success, error number otherwise
*/
static int hdd_extscan_epno_fill_network_list(
hdd_context_t *hddctx,
struct wifi_epno_params *req_msg,
struct nlattr **tb)
{
struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1];
struct nlattr *networks;
int rem1, ssid_len;
uint8_t index, *ssid;
uint32_t expected_networks;
expected_networks = req_msg->num_networks;
index = 0;
if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST]) {
hddLog(LOGE, FL("attr networks list failed"));
return -EINVAL;
}
nla_for_each_nested(networks,
tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST],
rem1) {
if (index == expected_networks) {
hddLog(LOGW, FL("ignoring excess networks"));
break;
}
if (nla_parse(network, QCA_WLAN_VENDOR_ATTR_PNO_MAX,
nla_data(networks), nla_len(networks),
wlan_hdd_pno_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch ssid */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]) {
hddLog(LOGE, FL("attr network ssid failed"));
return -EINVAL;
}
ssid_len = nla_len(
network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]);
/* nla_parse will detect overflow but not underflow */
if (0 == ssid_len) {
hddLog(LOGE, FL("zero ssid length"));
return -EINVAL;
}
/* Decrement by 1, don't count null character */
ssid_len--;
req_msg->networks[index].ssid.length = ssid_len;
hddLog(LOG1, FL("network ssid length %d"), ssid_len);
ssid = nla_data(network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID]);
vos_mem_copy(req_msg->networks[index].ssid.ssId, ssid, ssid_len);
hddLog(LOG1, FL("Ssid: %.*s"),
req_msg->networks[index].ssid.length,
req_msg->networks[index].ssid.ssId);
/* Parse and fetch epno flags */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]) {
hddLog(LOGE, FL("attr epno flags failed"));
return -EINVAL;
}
req_msg->networks[index].flags = nla_get_u8(
network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS]);
hddLog(LOG1, FL("flags %u"), req_msg->networks[index].flags);
/* Parse and fetch auth bit */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]) {
hddLog(LOGE, FL("attr auth bit failed"));
return -EINVAL;
}
req_msg->networks[index].auth_bit_field = nla_get_u8(
network[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT]);
hddLog(LOG1, FL("auth bit %u"),
req_msg->networks[index].auth_bit_field);
index++;
}
req_msg->num_networks = index;
return 0;
}
/**
* __wlan_hdd_cfg80211_set_epno_list() - epno set network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function reads the NL vendor attributes from %tb and
* fill in the epno request message.
*
* Return: 0 on success, error number otherwise
*/
static int __wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct wifi_epno_params *req_msg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[
QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1];
eHalStatus status;
uint32_t num_networks, len;
int ret_val;
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX,
data, data_len,
wlan_hdd_pno_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch number of networks */
if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]) {
hddLog(LOGE, FL("attr num networks failed"));
return -EINVAL;
}
/*
* num_networks is also used as EPNO SET/RESET request.
* if num_networks is zero then it is treated as RESET.
*/
num_networks = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS]);
if (num_networks > MAX_EPNO_NETWORKS) {
hddLog(LOG1,
FL("input num_networks: %d exceeded max: %d, hence reset to: %d"),
num_networks, MAX_EPNO_NETWORKS, MAX_EPNO_NETWORKS);
num_networks = MAX_EPNO_NETWORKS;
}
hddLog(LOG1, FL("num networks %u"), num_networks);
len = sizeof(*req_msg) +
(num_networks * sizeof(struct wifi_epno_network));
req_msg = vos_mem_malloc(len);
if (!req_msg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
vos_mem_zero(req_msg, len);
req_msg->num_networks = num_networks;
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
req_msg->request_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID]);
req_msg->session_id = adapter->sessionId;
hddLog(LOG1, FL("Req Id %u Session Id %d"),
req_msg->request_id, req_msg->session_id);
if (num_networks) {
/* Parse and fetch min_5ghz_rssi */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]) {
hddLog(LOGE, FL("min_5ghz_rssi id failed"));
goto fail;
}
req_msg->min_5ghz_rssi = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI]);
/* Parse and fetch min_24ghz_rssi */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]) {
hddLog(LOGE, FL("min_24ghz_rssi id failed"));
goto fail;
}
req_msg->min_24ghz_rssi = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI]);
/* Parse and fetch initial_score_max */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]) {
hddLog(LOGE, FL("initial_score_max id failed"));
goto fail;
}
req_msg->initial_score_max = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX]);
/* Parse and fetch current_connection_bonus */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS]) {
hddLog(LOGE, FL("current_connection_bonus id failed"));
goto fail;
}
req_msg->current_connection_bonus = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS]
);
/* Parse and fetch same_network_bonus */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]) {
hddLog(LOGE, FL("same_network_bonus id failed"));
goto fail;
}
req_msg->same_network_bonus = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS]);
/* Parse and fetch secure_bonus */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]) {
hddLog(LOGE, FL("secure_bonus id failed"));
goto fail;
}
req_msg->secure_bonus = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS]);
/* Parse and fetch band_5ghz_bonus */
if (!tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]) {
hddLog(LOGE, FL("band_5ghz_bonus id failed"));
goto fail;
}
req_msg->band_5ghz_bonus = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS]);
hddLog(LOG1,
FL("min_5ghz_rssi: %d min_24ghz_rssi: %d initial_score_max: %d current_connection_bonus: %d"),
req_msg->min_5ghz_rssi,
req_msg->min_24ghz_rssi,
req_msg->initial_score_max,
req_msg->current_connection_bonus);
hddLog(LOG1,
FL("same_network_bonus: %d secure_bonus: %d band_5ghz_bonus: %d"),
req_msg->same_network_bonus,
req_msg->secure_bonus,
req_msg->band_5ghz_bonus);
if (hdd_extscan_epno_fill_network_list(hdd_ctx, req_msg, tb))
goto fail;
}
status = sme_set_epno_list(hdd_ctx->hHal, req_msg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_set_epno_list failed(err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(req_msg);
return 0;
fail:
vos_mem_free(req_msg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_set_epno_list() - epno set network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function reads the NL vendor attributes from %tb and
* fill in the epno request message.
*
* Return: 0 on success, error number otherwise
*/
static int wlan_hdd_cfg80211_set_epno_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_epno_list(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_extscan_passpoint_fill_network_list() - passpoint fill network list
* @hddctx: HDD context
* @req_msg: request message
* @tb: vendor attribute table
*
* This function reads the network block NL vendor attributes from %tb and
* fill in the passpoint request message.
*
* Return: 0 on success, error number otherwise
*/
static int hdd_extscan_passpoint_fill_network_list(
hdd_context_t *hddctx,
struct wifi_passpoint_req *req_msg,
struct nlattr **tb)
{
struct nlattr *network[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1];
struct nlattr *networks;
int rem1, len;
uint8_t index;
uint32_t expected_networks;
expected_networks = req_msg->num_networks;
index = 0;
if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY]) {
hddLog(LOGE, FL("attr network array failed"));
return -EINVAL;
}
nla_for_each_nested(networks,
tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY],
rem1) {
if (index == expected_networks) {
hddLog(LOGW, FL("ignoring excess networks"));
break;
}
if (nla_parse(network,
QCA_WLAN_VENDOR_ATTR_PNO_MAX,
nla_data(networks), nla_len(networks),
wlan_hdd_pno_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch identifier */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID]) {
hddLog(LOGE, FL("attr passpoint id failed"));
return -EINVAL;
}
req_msg->networks[index].id = nla_get_u32(
network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID]);
hddLog(LOG1, FL("Id %u"), req_msg->networks[index].id);
/* Parse and fetch realm */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]) {
hddLog(LOGE, FL("attr realm failed"));
return -EINVAL;
}
len = nla_len(
network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]);
if (len < 0 || len > SIR_PASSPOINT_REALM_LEN) {
hddLog(LOGE, FL("Invalid realm size %d"), len);
return -EINVAL;
}
vos_mem_copy(req_msg->networks[index].realm,
nla_data(network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM]),
len);
hddLog(LOG1, FL("realm len %d"), len);
hddLog(LOG1, FL("realm: %s"), req_msg->networks[index].realm);
/* Parse and fetch roaming consortium ids */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID]) {
hddLog(LOGE, FL("attr roaming consortium ids failed"));
return -EINVAL;
}
nla_memcpy(&req_msg->networks[index].roaming_consortium_ids,
network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID],
sizeof(req_msg->networks[0].roaming_consortium_ids));
hddLog(LOG1, FL("roaming consortium ids"));
VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
req_msg->networks[index].roaming_consortium_ids,
sizeof(req_msg->networks[0].roaming_consortium_ids));
/* Parse and fetch plmn */
if (!network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN]) {
hddLog(LOGE, FL("attr plmn failed"));
return -EINVAL;
}
nla_memcpy(&req_msg->networks[index].plmn,
network[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN],
SIR_PASSPOINT_PLMN_LEN);
hddLog(LOG1, FL("plmn %02x:%02x:%02x"),
req_msg->networks[index].plmn[0],
req_msg->networks[index].plmn[1],
req_msg->networks[index].plmn[2]);
index++;
}
req_msg->num_networks = index;
return 0;
}
/**
* __wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function reads the NL vendor attributes from %tb and
* fill in the passpoint request message.
*
* Return: 0 on success, error number otherwise
*/
static int __wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct wifi_passpoint_req *req_msg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1];
eHalStatus status;
uint32_t num_networks = 0;
int ret;
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, data_len,
wlan_hdd_pno_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch number of networks */
if (!tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]) {
hddLog(LOGE, FL("attr num networks failed"));
return -EINVAL;
}
num_networks = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM]);
if (num_networks > SIR_PASSPOINT_LIST_MAX_NETWORKS) {
hddLog(LOGE, FL("num networks %u exceeds max %u"),
num_networks, SIR_PASSPOINT_LIST_MAX_NETWORKS);
return -EINVAL;
}
hddLog(LOG1, FL("num networks %u"), num_networks);
req_msg = vos_mem_malloc(sizeof(*req_msg) +
(num_networks * sizeof(req_msg->networks[0])));
if (!req_msg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
req_msg->num_networks = num_networks;
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
req_msg->request_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
req_msg->session_id = adapter->sessionId;
hddLog(LOG1, FL("Req Id %u Session Id %d"), req_msg->request_id,
req_msg->session_id);
if (hdd_extscan_passpoint_fill_network_list(hdd_ctx, req_msg, tb))
goto fail;
status = sme_set_passpoint_list(hdd_ctx->hHal, req_msg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_set_passpoint_list failed(err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(req_msg);
return 0;
fail:
vos_mem_free(req_msg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_set_passpoint_list() - set passpoint network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function reads the NL vendor attributes from %tb and
* fill in the passpoint request message.
*
* Return: 0 on success, error number otherwise
*/
static int wlan_hdd_cfg80211_set_passpoint_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_passpoint_list(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function resets passpoint networks list
*
* Return: 0 on success, error number otherwise
*/
static int __wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct wifi_passpoint_req *req_msg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PNO_MAX + 1];
eHalStatus status;
int ret;
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PNO_MAX, data, data_len,
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
req_msg = vos_mem_malloc(sizeof(*req_msg));
if (!req_msg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
req_msg->request_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
req_msg->session_id = adapter->sessionId;
hddLog(LOG1, FL("Req Id %u Session Id %d"),
req_msg->request_id, req_msg->session_id);
status = sme_reset_passpoint_list(hdd_ctx->hHal, req_msg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_reset_passpoint_list failed(err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(req_msg);
return 0;
fail:
vos_mem_free(req_msg);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_reset_passpoint_list() - reset passpoint network list
* @wiphy: wiphy
* @wdev: pointer to wireless dev
* @data: data pointer
* @data_len: data length
*
* This function resets passpoint networks list
*
* Return: 0 on success, error number otherwise
*/
static int wlan_hdd_cfg80211_reset_passpoint_list(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_reset_passpoint_list(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /* FEATURE_WLAN_EXTSCAN */
/**
* wlan_hdd_cfg80211_set_feature() - Set the bitmask for supported features
* @feature_flags: pointer to the byte array of features.
* @feature: Feature to be turned ON in the byte array.
*
* Return: None
*
* This is called to turn ON or SET the feature flag for the requested feature.
*/
#define NUM_BITS_IN_BYTE 8
void wlan_hdd_cfg80211_set_feature(uint8_t *feature_flags, uint8_t feature)
{
uint32_t index;
uint8_t bit_mask;
index = feature / NUM_BITS_IN_BYTE;
bit_mask = 1 << (feature % NUM_BITS_IN_BYTE);
feature_flags[index] |= bit_mask;
}
/**
* __wlan_hdd_cfg80211_get_features() - Get the Driver Supported features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send supported feature set to
* supplicant upon a request/query from the supplicant.
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_get_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb = NULL;
uint8_t feature_flags[(NUM_QCA_WLAN_VENDOR_FEATURES + 7) / 8] = {0};
int ret_val;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
ret_val = wlan_hdd_validate_context(pHddCtx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (pHddCtx->cfg_ini->isRoamOffloadEnabled) {
hddLog(LOG1, FL("Key Mgmt Offload is supported"));
wlan_hdd_cfg80211_set_feature (feature_flags,
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD);
}
#endif
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS,
sizeof(feature_flags), feature_flags))
goto nla_put_failure;
return cfg80211_vendor_cmd_reply(skb);
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_features() - Get the Driver Supported features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send supported feature set to
* supplicant upon a request/query from the supplicant.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_features(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
static bool put_wifi_rate_stat( tpSirWifiRateStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE,
stats->rate.preamble) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS,
stats->rate.nss) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW,
stats->rate.bw) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX,
stats->rate.rateMcsIdx) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE,
stats->rate.bitrate ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU,
stats->txMpdu ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU,
stats->rxMpdu ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST,
stats->mpduLost ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES,
stats->retries) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT,
stats->retriesShort ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG,
stats->retriesLong))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return FALSE;
}
return TRUE;
}
static bool put_wifi_peer_info( tpSirWifiPeerInfo stats,
struct sk_buff *vendor_event)
{
u32 i = 0;
tpSirWifiRateStat pRateStats;
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE,
stats->type) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS,
VOS_MAC_ADDR_SIZE, &stats->peerMacAddress[0]) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES,
stats->capabilities) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES,
stats->numRate))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
goto error;
}
if (stats->numRate)
{
struct nlattr *rateInfo;
struct nlattr *rates;
rateInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO);
if (rateInfo == NULL)
goto error;
for (i = 0; i < stats->numRate; i++)
{
pRateStats = (tpSirWifiRateStat )((uint8 *)
stats->rateStats +
(i * sizeof(tSirWifiRateStat)));
rates = nla_nest_start(vendor_event, i);
if (rates == NULL)
goto error;
if (FALSE == put_wifi_rate_stat(pRateStats, vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return FALSE;
}
nla_nest_end(vendor_event, rates);
}
nla_nest_end(vendor_event, rateInfo);
}
return TRUE;
error:
return FALSE;
}
static bool put_wifi_wmm_ac_stat( tpSirWifiWmmAcStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC,
stats->ac ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU,
stats->pending_msdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU,
stats->txMpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU,
stats->rxMpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST,
stats->txMcast ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST,
stats->rxMcast ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU,
stats->rxAmpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU,
stats->txAmpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST,
stats->mpduLost )||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES,
stats->retries ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT,
stats->retriesShort ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG,
stats->retriesLong ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN,
stats->contentionTimeMin ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX,
stats->contentionTimeMax ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG,
stats->contentionTimeAvg ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES,
stats->contentionNumSamples ))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
return TRUE;
}
static bool put_wifi_interface_info(tpSirWifiInterfaceInfo stats,
struct sk_buff *vendor_event)
{
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE,
stats->mode ) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR,
VOS_MAC_ADDR_SIZE, stats->macAddr) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE,
stats->state ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING,
stats->roaming ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES,
stats->capabilities ) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID,
strlen(stats->ssid), stats->ssid) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID,
VOS_MAC_ADDR_SIZE, stats->bssid) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->apCountryStr) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->countryStr))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
return TRUE;
}
static bool put_wifi_iface_stats(tpSirWifiIfaceStat pWifiIfaceStat,
u32 num_peers,
struct sk_buff *vendor_event)
{
int i = 0;
struct nlattr *wmmInfo;
struct nlattr *wmmStats;
u64 average_tsf_offset;
if (FALSE == put_wifi_interface_info(
&pWifiIfaceStat->info,
vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
average_tsf_offset = pWifiIfaceStat->avg_bcn_spread_offset_high;
average_tsf_offset = (average_tsf_offset << 32) |
pWifiIfaceStat->avg_bcn_spread_offset_low ;
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_IFACE) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
num_peers) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX,
pWifiIfaceStat->beaconRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX,
pWifiIfaceStat->mgmtRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX,
pWifiIfaceStat->mgmtActionRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX,
pWifiIfaceStat->mgmtActionTx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT,
pWifiIfaceStat->rssiMgmt) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA,
pWifiIfaceStat->rssiData) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK,
pWifiIfaceStat->rssiAck) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED,
pWifiIfaceStat->is_leaky_ap) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED,
pWifiIfaceStat->avg_rx_frms_leaked) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME,
pWifiIfaceStat->rx_leak_window) ||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
nla_put_u64_64bit(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET,
average_tsf_offset,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PAD) ||
#else
nla_put_u64(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET,
average_tsf_offset) ||
#endif
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT,
pWifiIfaceStat->rts_succ_cnt) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT,
pWifiIfaceStat->rts_fail_cnt) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT,
pWifiIfaceStat->ppdu_succ_cnt) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT,
pWifiIfaceStat->ppdu_fail_cnt))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return FALSE;
}
wmmInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO);
if (wmmInfo == NULL)
return FALSE;
for (i = 0; i < WIFI_AC_MAX; i++)
{
wmmStats = nla_nest_start(vendor_event, i);
if (wmmStats == NULL)
return FALSE;
if (FALSE == put_wifi_wmm_ac_stat(
&pWifiIfaceStat->AccessclassStats[i],
vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("put_wifi_wmm_ac_stat Fail"));
return FALSE;
}
nla_nest_end(vendor_event, wmmStats);
}
nla_nest_end(vendor_event, wmmInfo);
return TRUE;
}
static tSirWifiInterfaceMode
hdd_map_device_to_ll_iface_mode ( int deviceMode )
{
switch (deviceMode)
{
case WLAN_HDD_INFRA_STATION:
return WIFI_INTERFACE_STA;
case WLAN_HDD_SOFTAP:
return WIFI_INTERFACE_SOFTAP;
case WLAN_HDD_P2P_CLIENT:
return WIFI_INTERFACE_P2P_CLIENT;
case WLAN_HDD_P2P_GO:
return WIFI_INTERFACE_P2P_GO;
case WLAN_HDD_IBSS:
return WIFI_INTERFACE_IBSS;
default:
/* Return Interface Mode as STA for all the unsupported modes */
return WIFI_INTERFACE_STA;
}
}
static bool hdd_get_interface_info(hdd_adapter_t *pAdapter,
tpSirWifiInterfaceInfo pInfo)
{
v_U8_t *staMac = NULL;
hdd_station_ctx_t *pHddStaCtx;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
pInfo->mode = hdd_map_device_to_ll_iface_mode(pAdapter->device_mode);
vos_mem_copy(pInfo->macAddr,
pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)))
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_NotConnected == pHddStaCtx->conn_info.connState)
{
pInfo->state = WIFI_DISCONNECTED;
}
if (eConnectionState_Connecting == pHddStaCtx->conn_info.connState)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Session ID %d, Connection is in progress", __func__,
pAdapter->sessionId);
pInfo->state = WIFI_ASSOCIATING;
}
if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
(VOS_FALSE == pHddStaCtx->conn_info.uIsAuthenticated))
{
staMac = (v_U8_t *) &(pAdapter->macAddressCurrent.bytes[0]);
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: client " MAC_ADDRESS_STR
" is in the middle of WPS/EAPOL exchange.", __func__,
MAC_ADDR_ARRAY(staMac));
pInfo->state = WIFI_AUTHENTICATING;
}
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
{
pInfo->state = WIFI_ASSOCIATED;
vos_mem_copy(pInfo->bssid,
&pHddStaCtx->conn_info.bssId, VOS_MAC_ADDR_SIZE);
vos_mem_copy(pInfo->ssid,
pHddStaCtx->conn_info.SSID.SSID.ssId,
pHddStaCtx->conn_info.SSID.SSID.length);
/*
* NULL Terminate the string
*/
pInfo->ssid[pHddStaCtx->conn_info.SSID.SSID.length] = 0;
}
}
vos_mem_copy(pInfo->countryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
vos_mem_copy(pInfo->apCountryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
return TRUE;
}
/*
* hdd_link_layer_process_peer_stats () - This function is called after
* receiving Link Layer Peer statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static void hdd_link_layer_process_peer_stats(hdd_adapter_t *pAdapter,
u32 more_data,
tpSirWifiPeerStat pData)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tpSirWifiPeerStat pWifiPeerStat;
tpSirWifiPeerInfo pWifiPeerInfo;
struct sk_buff *vendor_event;
int status, i;
struct nlattr *peers;
int numRate;
ENTER();
pWifiPeerStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return;
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_PEER_ALL : numPeers %u, more data = %u",
pWifiPeerStat->numPeers,
more_data);
/*
* Allocate a size of 4096 for the peer stats comprising
* each of size = sizeof (tSirWifiPeerInfo) + numRate *
* sizeof (tSirWifiRateStat).Each field is put with an
* NL attribute.The size of 4096 is considered assuming
* that number of rates shall not exceed beyond 50 with
* the sizeof (tSirWifiRateStat) being 32.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_cmd_alloc_reply_skb failed",
__func__);
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_PEER) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
more_data) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
pWifiPeerStat->numPeers))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR put fail", __func__);
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8 *)
pWifiPeerStat->peerInfo);
if (pWifiPeerStat->numPeers)
{
struct nlattr *peerInfo;
peerInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO);
if (peerInfo == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
for (i = 1; i <= pWifiPeerStat->numPeers; i++)
{
peers = nla_nest_start(vendor_event, i);
if (peers == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
numRate = pWifiPeerInfo->numRate;
if (FALSE == put_wifi_peer_info(
pWifiPeerInfo, vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put_wifi_peer_info fail"));
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8 *)
pWifiPeerStat->peerInfo +
(i * sizeof(tSirWifiPeerInfo)) +
(numRate * sizeof (tSirWifiRateStat)));
nla_nest_end(vendor_event, peers);
}
nla_nest_end(vendor_event, peerInfo);
}
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
}
/*
* hdd_link_layer_process_iface_stats () - This function is called after
* receiving Link Layer Interface statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static void hdd_link_layer_process_iface_stats(hdd_adapter_t *pAdapter,
tpSirWifiIfaceStat pData,
u32 num_peers)
{
tpSirWifiIfaceStat pWifiIfaceStat;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status;
ENTER();
pWifiIfaceStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return;
/*
* Allocate a size of 4096 for the interface stats comprising
* sizeof (tpSirWifiIfaceStat).The size of 4096 is considered
* assuming that all these fit with in the limit.Please take
* a call on the limit based on the data requirements on
* interface statistics.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed") );
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "WMI_LINK_STATS_IFACE Data");
if (FALSE == hdd_get_interface_info(pAdapter,
&pWifiIfaceStat->info))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("hdd_get_interface_info get fail"));
kfree_skb(vendor_event);
return;
}
if (FALSE == put_wifi_iface_stats(pWifiIfaceStat, num_peers, vendor_event)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("put_wifi_iface_stats fail"));
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
}
/*
* hdd_link_layer_process_radio_stats () - This function is called after
* receiving Link Layer Radio statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static void hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter,
u32 more_data,
tpSirWifiRadioStat pData,
u32 num_radio)
{
int status, i;
tpSirWifiRadioStat pWifiRadioStat;
tpSirWifiChannelStats pWifiChannelStats;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
pWifiRadioStat = pData;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return;
hddLog(LOG1,
"LL_STATS_RADIO"
" number of radios: %u radio: %d onTime: %u"
" txTime: %u rxTime: %u onTimeScan: %u onTimeNbd: %u"
" onTimeGscan: %u onTimeRoamScan: %u"
" onTimePnoScan: %u onTimeHs20: %u"
" numChannels: %u total_num_tx_power_levels: %u",
num_radio, pWifiRadioStat->radio, pWifiRadioStat->onTime,
pWifiRadioStat->txTime, pWifiRadioStat->rxTime,
pWifiRadioStat->onTimeScan, pWifiRadioStat->onTimeNbd,
pWifiRadioStat->onTimeGscan, pWifiRadioStat->onTimeRoamScan,
pWifiRadioStat->onTimePnoScan, pWifiRadioStat->onTimeHs20,
pWifiRadioStat->numChannels,
pWifiRadioStat->total_num_tx_power_levels);
/*
* Allocate a size of 4096 for the Radio stats comprising
* sizeof (tSirWifiRadioStat) + numChannels * sizeof
* (tSirWifiChannelStats).Each channel data is put with an
* NL attribute.The size of 4096 is considered assuming that
* number of channels shall not exceed beyond 60 with the
* sizeof (tSirWifiChannelStats) being 24 bytes.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA,
more_data) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
num_radio) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
pWifiRadioStat->radio) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
pWifiRadioStat->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
pWifiRadioStat->txTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
pWifiRadioStat->rxTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
pWifiRadioStat->onTimeScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
pWifiRadioStat->onTimeNbd) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN,
pWifiRadioStat->onTimeGscan)||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
pWifiRadioStat->onTimeRoamScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
pWifiRadioStat->onTimePnoScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
pWifiRadioStat->onTimeHs20) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS,
pWifiRadioStat->total_num_tx_power_levels) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
pWifiRadioStat->numChannels)) {
hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail"));
kfree_skb(vendor_event);
return ;
}
if (pWifiRadioStat->total_num_tx_power_levels) {
if (nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL,
sizeof(u32) * pWifiRadioStat->total_num_tx_power_levels,
pWifiRadioStat->tx_time_per_power_level)) {
hddLog(LOGE, FL("ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL put fail"));
kfree_skb(vendor_event);
return;
}
}
if (pWifiRadioStat->numChannels)
{
struct nlattr *chList;
struct nlattr *chInfo;
chList = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
if (chList == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
for (i = 0; i < pWifiRadioStat->numChannels; i++)
{
pWifiChannelStats = (tpSirWifiChannelStats) ((uint8*)
pWifiRadioStat->channels +
(i * sizeof(tSirWifiChannelStats)));
chInfo = nla_nest_start(vendor_event, i);
if (chInfo == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
kfree_skb(vendor_event);
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
pWifiChannelStats->channel.width) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
pWifiChannelStats->channel.centerFreq) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
pWifiChannelStats->channel.centerFreq0) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
pWifiChannelStats->channel.centerFreq1) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
pWifiChannelStats->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
pWifiChannelStats->ccaBusyTime))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_put failed"));
kfree_skb(vendor_event);
return ;
}
nla_nest_end(vendor_event, chInfo);
}
nla_nest_end(vendor_event, chList);
}
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
}
/*
* wlan_hdd_cfg80211_link_layer_stats_callback () - This function is called
* after receiving Link Layer indications from FW.This callback converts the
* firmware data to the NL data and send the same to the kernel/upper layers.
*/
static void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx, int indType,
void *pRsp)
{
hdd_adapter_t *pAdapter = NULL;
struct hdd_ll_stats_context *context;
hdd_context_t *pHddCtx = ctx;
tpSirLLStatsResults linkLayerStatsResults = (tpSirLLStatsResults)pRsp;
int status;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return;
pAdapter = hdd_get_adapter_by_vdev(pHddCtx,
linkLayerStatsResults->ifaceId);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: vdev_id %d does not exist with host",
__func__, linkLayerStatsResults->ifaceId);
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Link Layer Indication indType: %d", __func__, indType);
switch (indType)
{
case SIR_HAL_LL_STATS_RESULTS_RSP:
{
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS RESP paramID = 0x%x, ifaceId = %u respId = %u, moreResultToFollow = %u, num radio = %u result = %pK",
linkLayerStatsResults->paramId, linkLayerStatsResults->ifaceId,
linkLayerStatsResults->rspId,
linkLayerStatsResults->moreResultToFollow,
linkLayerStatsResults->num_radio,
linkLayerStatsResults->results);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ll_stats_context;
/* validate response received from target */
if ((context->request_id != linkLayerStatsResults->rspId) ||
!(context->request_bitmap & linkLayerStatsResults->paramId)) {
spin_unlock(&hdd_context_lock);
hddLog(LOGE,
FL("Error : Request id %d response id %d request bitmap 0x%x response bitmap 0x%x"),
context->request_id, linkLayerStatsResults->rspId,
context->request_bitmap, linkLayerStatsResults->paramId);
return;
}
spin_unlock(&hdd_context_lock);
if (linkLayerStatsResults->paramId & WMI_LINK_STATS_RADIO )
{
hdd_link_layer_process_radio_stats(pAdapter,
linkLayerStatsResults->moreResultToFollow,
(tpSirWifiRadioStat)
linkLayerStatsResults->results,
linkLayerStatsResults->num_radio);
spin_lock(&hdd_context_lock);
if (!linkLayerStatsResults->moreResultToFollow)
context->request_bitmap &= ~(WMI_LINK_STATS_RADIO);
spin_unlock(&hdd_context_lock);
}
else if (linkLayerStatsResults->paramId & WMI_LINK_STATS_IFACE )
{
hdd_link_layer_process_iface_stats(pAdapter,
(tpSirWifiIfaceStat)
linkLayerStatsResults->results,
linkLayerStatsResults->num_peers);
spin_lock(&hdd_context_lock);
/* Firmware doesn't send peerstats event if no peers are
* connected. HDD should not wait for any peerstats in this case
* and return the status to middlewre after receiving iface
* stats
*/
if (!linkLayerStatsResults->num_peers)
context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER);
context->request_bitmap &= ~(WMI_LINK_STATS_IFACE);
spin_unlock(&hdd_context_lock);
}
else if (linkLayerStatsResults->paramId & WMI_LINK_STATS_ALL_PEER )
{
hdd_link_layer_process_peer_stats(pAdapter,
linkLayerStatsResults->moreResultToFollow,
(tpSirWifiPeerStat)
linkLayerStatsResults->results);
spin_lock(&hdd_context_lock);
if (!linkLayerStatsResults->moreResultToFollow)
context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER);
spin_unlock(&hdd_context_lock);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("INVALID LL_STATS_NOTIFY RESPONSE ***********"));
}
spin_lock(&hdd_context_lock);
/* complete response event if all requests bitmaps are cleared */
if (0 == context->request_bitmap)
complete(&context->response_event);
spin_unlock(&hdd_context_lock);
break;
}
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "invalid event type %d", indType);
break;
}
return;
}
static const struct nla_policy
qca_wlan_vendor_ll_ext_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR] = {
.type = NLA_U32
},
[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF] = {
.type = NLA_U32
},
};
/**
* __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters
* @wiphy: wiphy handle
* @wdev: wdev handle
* @data: user layer input
* @data_len: length of user layer input
*
* This function is called in SSR protected environment.
*
* Return: 0 Success, EINVAL failure
*/
static int __wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX + 1];
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct sir_ll_ext_stats_threshold thresh = {0,};
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ENTER();
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
hddLog(VOS_TRACE_LEVEL_INFO_LOW,
FL("Get user layer settings for LL stat. Length is %d bytes"),
data_len);
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX,
(struct nlattr *)data, data_len,
qca_wlan_vendor_ll_ext_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("maximum attribute not present"));
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]) {
thresh.period = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD]);
if (thresh.period != 0 && thresh.period < LL_STATS_MIN_PERIOD)
thresh.period = LL_STATS_MIN_PERIOD;
if (thresh.period > LL_STATS_MAX_PERIOD)
thresh.period = LL_STATS_MAX_PERIOD;
} else {
thresh.period = LL_STATS_INVALID_PERIOD;
hddLog(VOS_TRACE_LEVEL_INFO,
FL("No MAC counter period parameter"));
}
/* period==0. Just disable mac counter */
if (thresh.period == 0) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Mac counter will be disaabled"));
goto set_param;
}
/* global thresh is not enabled */
if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]) {
thresh.global = false;
hddLog(VOS_TRACE_LEVEL_WARN, FL("Global thresh is not set"));
} else {
thresh.global_threshold = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD]);
thresh.global = true;
hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("thresh is %d"),
thresh.global_threshold);
}
if (!tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]) {
thresh.global = false;
hddLog(VOS_TRACE_LEVEL_WARN, FL("Global thresh is not set"));
} else {
thresh.global = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL]);
hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("Global is %d"),
thresh.global);
}
thresh.enable_bitmap = false;
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]) {
thresh.tx_bitmap = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP]);
thresh.enable_bitmap = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]) {
thresh.rx_bitmap = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP]);
thresh.enable_bitmap = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]) {
thresh.cca_bitmap = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP]);
thresh.enable_bitmap = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]) {
thresh.signal_bitmap = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP]);
thresh.enable_bitmap = true;
}
if (!thresh.global && !thresh.enable_bitmap) {
hddLog(VOS_TRACE_LEVEL_WARN, FL("No thresh exits."));
thresh.enable = false;
} else {
thresh.enable = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]) {
thresh.tx.msdu = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]) {
thresh.tx.mpdu = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]) {
thresh.tx.ppdu = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]) {
thresh.tx.bytes = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]) {
thresh.tx.msdu_drop = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]) {
thresh.tx.byte_drop = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]) {
thresh.tx.mpdu_retry = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]) {
thresh.tx.mpdu_fail = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]) {
thresh.tx.ppdu_fail = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]) {
thresh.tx.aggregation = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]) {
thresh.tx.succ_mcs = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]) {
thresh.tx.fail_mcs = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]) {
thresh.tx.delay = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]) {
thresh.rx.mpdu = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]) {
thresh.rx.bytes = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]) {
thresh.rx.ppdu = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]) {
thresh.rx.ppdu_bytes = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]) {
thresh.rx.mpdu_lost = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]) {
thresh.rx.mpdu_retry = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]) {
thresh.rx.mpdu_dup = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]) {
thresh.rx.mpdu_discard = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]) {
thresh.rx.aggregation = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]) {
thresh.rx.mcs = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]) {
thresh.rx.ps_inds = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]) {
thresh.rx.ps_durs = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]) {
thresh.rx.probe_reqs = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]) {
thresh.rx.other_mgmt = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]) {
thresh.cca.idle_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]) {
thresh.cca.tx_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]) {
thresh.cca.rx_in_bss_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]) {
thresh.cca.rx_out_bss_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]) {
thresh.cca.rx_busy_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]) {
thresh.cca.rx_in_bad_cond_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]) {
thresh.cca.tx_in_bad_cond_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]) {
thresh.cca.wlan_not_avail_time = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]) {
thresh.signal.snr = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR]);
}
if (tb[QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]) {
thresh.signal.nf = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF]);
}
set_param:
hddLog(VOS_TRACE_LEVEL_INFO_LOW, FL("Send thresh settings to target"));
if (eHAL_STATUS_SUCCESS != sme_ll_stats_set_thresh(hdd_ctx->hHal,
&thresh)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ll_stats_set_thresh failed."));
return -EINVAL;
}
EXIT();
return 0;
}
/**
* __wlan_hdd_cfg80211_ll_stats_ext_set_param - config monitor parameters
* @wiphy: wiphy handle
* @wdev: wdev handle
* @data: user layer input
* @data_len: length of user layer input
*
* Return: 0 Success, EINVAL failure
*/
int wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_ext_set_param(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* put_per_peer_ps_info() - put per peer sta's PS info into nl80211 msg
* @wifi_peer_info: peer information
* @vendor_event: buffer for vendor event
*
* Return: 0 success
*/
static int put_per_peer_ps_info(tSirWifiPeerInfo *wifi_peer_info,
struct sk_buff *vendor_event)
{
if (!wifi_peer_info) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Invalid pointer to peer info."));
return -EINVAL;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE,
wifi_peer_info->power_saving) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS,
VOS_MAC_ADDR_SIZE, wifi_peer_info->peerMacAddress)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
return 0;
}
/**
* put_wifi_peer_ps_info() - Put peer sta's power state into nl80211 msg
* @data - stats for peer STA
* @vendor_event - buffer for vendor event
*
* Return: 0 success
*/
static int put_wifi_peer_ps_info(tSirWifiPeerStat *data,
struct sk_buff *vendor_event)
{
uint32_t peer_num, i;
tSirWifiPeerInfo *wifi_peer_info;
struct nlattr *peer_info, *peers;
if (!data) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Invalid pointer to Wifi peer stat."));
return -EINVAL;
}
peer_num = data->numPeers;
if (peer_num == 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Peer number is zero."));
return -EINVAL;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM,
peer_num)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
peers = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG);
if (peers == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
for (i = 0; i < peer_num; i++) {
wifi_peer_info = &data->peerInfo[i];
peer_info = nla_nest_start(vendor_event, i);
if (peer_info == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
if (put_per_peer_ps_info(wifi_peer_info, vendor_event))
return -EINVAL;
nla_nest_end(vendor_event, peer_info);
}
nla_nest_end(vendor_event, peers);
return 0;
}
/**
* put_tx_failure_info() - Put TX failure info
* @tx_fail - TX failure info
* @skb - buffer for vendor event
*
* Return: 0 Success
*/
static inline int put_tx_failure_info(struct sir_wifi_iface_tx_fail *tx_fail,
struct sk_buff *skb)
{
int status = 0;
if (!tx_fail || !skb)
return -EINVAL;
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID,
tx_fail->tid) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU,
tx_fail->msdu_num) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS,
tx_fail->status)) {
hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail"));
status = -EINVAL;
}
return status;
}
/**
* put_wifi_channel_cca_info() - put channel cca info to vendor event
* @info: cca info array for all channels
* @vendor_event: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_channel_cca_info(struct sir_wifi_chan_cca_stats *cca,
struct sk_buff *vendor_event)
{
/* There might be no CCA info for a channel */
if (cca == NULL)
return 0;
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME,
cca->idle_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME,
cca->tx_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME,
cca->rx_in_bss_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME,
cca->rx_out_bss_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY,
cca->rx_busy_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD,
cca->rx_in_bad_cond_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD,
cca->tx_in_bad_cond_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL,
cca->wlan_not_avail_time) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID,
cca->vdev_id)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
return 0;
}
/**
* put_wifi_signal_info - put chain signal info
* @info: RF chain signal info
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_signal_info(struct sir_wifi_peer_signal_stats *peer_signal,
struct sk_buff *skb)
{
uint32_t i, chain_count;
struct nlattr *chains, *att;
/* There might be no signal info for a peer */
if (peer_signal == NULL)
return 0;
chain_count = peer_signal->num_chain < WIFI_MAX_CHAINS ?
peer_signal->num_chain : WIFI_MAX_CHAINS;
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM,
chain_count)) {
hddLog(LOGE, FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
att = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL);
if (att == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
for (i = 0; i < chain_count; i++) {
chains = nla_nest_start(skb, i);
if (chains == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
hddLog(LOG2,
FL("SNR=%d, NF=%d, RX=%d, TX=%d"),
peer_signal->per_ant_snr[i],
peer_signal->nf[i],
peer_signal->per_ant_rx_mpdus[i],
peer_signal->per_ant_tx_mpdus[i]);
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR,
peer_signal->per_ant_snr[i]) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF,
peer_signal->nf[i]) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU,
peer_signal->per_ant_rx_mpdus[i]) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU,
peer_signal->per_ant_tx_mpdus[i])) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
nla_nest_end(skb, chains);
}
nla_nest_end(skb, att);
return 0;
}
/**
* put_wifi_wmm_ac_tx_info() - put AC TX info
* @info: tx info
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_wmm_ac_tx_info(struct sir_wifi_tx *tx_stats,
struct sk_buff *skb)
{
uint32_t *agg_size, *succ_mcs, *fail_mcs, *delay;
/* There might be no TX info for a peer */
if (tx_stats == NULL)
return 0;
agg_size = tx_stats->mpdu_aggr_size;
succ_mcs = tx_stats->success_mcs;
fail_mcs = tx_stats->fail_mcs;
delay = tx_stats->delay;
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU,
tx_stats->msdus) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU,
tx_stats->mpdus) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU,
tx_stats->ppdus) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES,
tx_stats->bytes) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP,
tx_stats->drops) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES,
tx_stats->drop_bytes) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY,
tx_stats->retries) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK,
tx_stats->failed) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM,
tx_stats->aggr_len) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM,
tx_stats->success_mcs_len) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM,
tx_stats->fail_mcs_len) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE,
tx_stats->delay_len))
goto put_attr_fail;
if (agg_size) {
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR,
tx_stats->aggr_len, agg_size))
goto put_attr_fail;
}
if (succ_mcs) {
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS,
tx_stats->success_mcs_len, succ_mcs))
goto put_attr_fail;
}
if (fail_mcs) {
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS,
tx_stats->fail_mcs_len, fail_mcs))
goto put_attr_fail;
}
if (delay) {
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY,
tx_stats->delay_len, delay))
goto put_attr_fail;
}
return 0;
put_attr_fail:
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
/**
* put_wifi_wmm_ac_rx_info() - put AC RX info
* @info: rx info
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_wmm_ac_rx_info(struct sir_wifi_rx *rx_stats,
struct sk_buff *skb)
{
uint32_t *mcs, *aggr;
/* There might be no RX info for a peer */
if (rx_stats == NULL)
return 0;
aggr = rx_stats->mpdu_aggr;
mcs = rx_stats->mcs;
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU,
rx_stats->mpdus) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES,
rx_stats->bytes) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU,
rx_stats->ppdus) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES,
rx_stats->ppdu_bytes) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST,
rx_stats->mpdu_lost) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY,
rx_stats->mpdu_retry) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP,
rx_stats->mpdu_dup) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD,
rx_stats->mpdu_discard) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM,
rx_stats->aggr_len) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM,
rx_stats->mcs_len))
goto put_attr_fail;
if (aggr) {
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR,
rx_stats->aggr_len, aggr))
goto put_attr_fail;
}
if (mcs) {
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS,
rx_stats->mcs_len, mcs))
goto put_attr_fail;
}
return 0;
put_attr_fail:
hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
/**
* put_wifi_wmm_ac_info() - put WMM AC info
* @info: per AC stats
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_wmm_ac_info(struct sir_wifi_ll_ext_wmm_ac_stats *ac_stats,
struct sk_buff *skb)
{
struct nlattr *wmm;
wmm = nla_nest_start(skb, ac_stats->type);
if (wmm == NULL)
goto nest_start_fail;
if (put_wifi_wmm_ac_tx_info(ac_stats->tx_stats, skb) ||
put_wifi_wmm_ac_rx_info(ac_stats->rx_stats, skb))
goto put_attr_fail;
nla_nest_end(skb, wmm);
return 0;
nest_start_fail:
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
put_attr_fail:
hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
/**
* put_wifi_ll_ext_peer_info() - put per peer info
* @info: peer stats
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_ll_ext_peer_info(struct sir_wifi_ll_ext_peer_stats *peers,
struct sk_buff *skb)
{
uint32_t i;
struct nlattr *wmm_ac;
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID,
peers->peer_id) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID,
peers->vdev_id) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES,
peers->sta_ps_inds) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION,
peers->sta_ps_durs) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ,
peers->rx_probe_reqs) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT,
peers->rx_oth_mgmts) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS,
VOS_MAC_ADDR_SIZE, peers->mac_address) ||
put_wifi_signal_info(&peers->peer_signal_stats, skb)) {
hddLog(LOGE, FL("put peer signal attr failed"));
return -EINVAL;
}
wmm_ac = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS);
if (wmm_ac == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
for (i = 0; i < WLAN_MAX_AC; i++) {
if (put_wifi_wmm_ac_info(&peers->ac_stats[i], skb)) {
hddLog(LOGE, FL("put WMM AC attr failed"));
return -EINVAL;
}
}
nla_nest_end(skb, wmm_ac);
return 0;
}
/**
* put_wifi_ll_ext_stats() - put link layer extension stats
* @info: link layer stats
* @skb: vendor event buffer
*
* Return: 0 Success, EINVAL failure
*/
static int put_wifi_ll_ext_stats(struct sir_wifi_ll_ext_stats *stats,
struct sk_buff *skb)
{
uint32_t i;
struct nlattr *peer, *peer_info, *channels, *channel_info;
struct sir_wifi_ll_ext_period *period;
period = &stats->time_stamp;
if (hdd_wlan_nla_put_u64(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
period->end_time) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
period->duration) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE,
stats->trigger_cond_id) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP,
stats->cca_chgd_bitmap) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP,
stats->sig_chgd_bitmap) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP,
stats->tx_chgd_bitmap) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP,
stats->rx_chgd_bitmap) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM,
stats->channel_num) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM,
stats->peer_num)) {
goto put_attr_fail;
}
channels = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS);
if (channels == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
for (i = 0; i < stats->channel_num; i++) {
channel_info = nla_nest_start(skb, i);
if (channel_info == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
if (put_wifi_channel_cca_info(&stats->cca[i], skb))
goto put_attr_fail;
nla_nest_end(skb, channel_info);
}
nla_nest_end(skb, channels);
peer_info = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER);
if (peer_info == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
for (i = 0; i < stats->peer_num; i++) {
peer = nla_nest_start(skb, i);
if (peer == NULL) {
hddLog(LOGE, FL("nla_nest_start failed"));
return -EINVAL;
}
if (put_wifi_ll_ext_peer_info(&stats->peer_stats[i], skb))
goto put_attr_fail;
nla_nest_end(skb, peer);
}
nla_nest_end(skb, peer_info);
return 0;
put_attr_fail:
hddLog(VOS_TRACE_LEVEL_ERROR, FL("QCA_WLAN_VENDOR_ATTR put fail"));
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_ll_stats_ext_callback() - LL stats callback
* @rsp - msg from FW
*
* An extension of wlan_hdd_cfg80211_link_layer_stats_callback.
* It converts monitoring parameters offloaded to NL data and send the same
* to the kerbel/upper layer.
*/
static void wlan_hdd_cfg80211_ll_stats_ext_callback(tSirLLStatsResults *rsp)
{
hdd_context_t *hdd_ctx;
struct sk_buff *skb;
int flags = vos_get_gfp_flags();
uint32_t param_id, index;
hdd_adapter_t *adapter;
tSirWifiPeerStat *peer_stats;
uint8_t *results;
int status, len;
v_CONTEXT_t vos_context = vos_get_global_context(0, NULL);
ENTER();
if (!rsp) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid result."));
return;
}
hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_context);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
adapter = hdd_get_adapter_by_vdev(hdd_ctx, rsp->ifaceId);
if (NULL == adapter) {
hddLog(LOGE, FL("vdev_id %d does not exist with host"),
rsp->ifaceId);
return;
}
index = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX;
len = LL_STATS_EVENT_BUF_SIZE + NLMSG_HDRLEN;
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL, len,
index, flags);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return;
}
results = rsp->results;
param_id = rsp->paramId;
hddLog(LOG1,
FL("LL_STATS RESP paramID = 0x%x, ifaceId = %u, result = %pK"),
rsp->paramId, rsp->ifaceId, rsp->results);
if (param_id & WMI_LL_STATS_EXT_PS_CHG) {
peer_stats = (tSirWifiPeerStat *)results;
status = put_wifi_peer_ps_info(peer_stats, skb);
} else if (param_id & WMI_LL_STATS_EXT_TX_FAIL) {
struct sir_wifi_iface_tx_fail *tx_fail;
tx_fail = (struct sir_wifi_iface_tx_fail *)results;
status = put_tx_failure_info(tx_fail, skb);
} else if (param_id & WMI_LL_STATS_EXT_MAC_COUNTER) {
hddLog(LOGE, FL("MAC counters stats"));
status = put_wifi_ll_ext_stats((struct sir_wifi_ll_ext_stats *)
rsp->results, skb);
} else {
hddLog(LOGE, FL("Unknown link layer stats"));
status = -1;
}
if (status == 0)
cfg80211_vendor_event(skb, flags);
else
kfree_skb(skb);
EXIT();
}
void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx)
{
sme_SetLinkLayerStatsIndCB(pHddCtx->hHal,
wlan_hdd_cfg80211_link_layer_stats_callback);
sme_set_ll_ext_cb(pHddCtx->hHal,
wlan_hdd_cfg80211_ll_stats_ext_callback);
}
const struct
nla_policy
qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] =
{ .type = NLA_U32 },
};
static int __wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1];
tSirLLStatsSetReq LinkLayerStatsSetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_set_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("maximum attribute not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("MPDU size Not present"));
return -EINVAL;
}
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Stats Gathering Not Present"));
return -EINVAL;
}
/* Shall take the request Id if the Upper layers pass. 1 For now.*/
LinkLayerStatsSetReq.reqId = 1;
LinkLayerStatsSetReq.mpduSizeThreshold =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]);
LinkLayerStatsSetReq.aggressiveStatisticsGathering =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]);
LinkLayerStatsSetReq.staId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_SET reqId = %d, staId = %d, mpduSizeThreshold = %d Statistics Gathering = %d ",
LinkLayerStatsSetReq.reqId, LinkLayerStatsSetReq.staId,
LinkLayerStatsSetReq.mpduSizeThreshold,
LinkLayerStatsSetReq.aggressiveStatisticsGathering);
if (eHAL_STATUS_SUCCESS != sme_LLStatsSetReq(pHddCtx->hHal,
&LinkLayerStatsSetReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"sme_LLStatsSetReq Failed", __func__);
return -EINVAL;
}
pAdapter->isLinkLayerStatsSet = 1;
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_ll_stats_set() - set ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
static int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_set(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX +1] =
{
/* Unsigned 32bit value provided by the caller issuing the GET stats
* command. When reporting
* the stats results, the driver uses the same value to indicate
* which GET request the results
* correspond to.
*/
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = { .type = NLA_U32 },
/* Unsigned 32bit value . bit mask to identify what statistics are
requested for retrieval */
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = { .type = NLA_U32 }
};
static int __wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
unsigned long rc;
struct hdd_ll_stats_context *context;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1];
tSirLLStatsGetReq LinkLayerStatsGetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
int status;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
/* ENTER() intentionally not used in a frequently invoked API */
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL ;
if (!pAdapter->isLinkLayerStatsSet) {
hddLog(LOGW, FL("isLinkLayerStatsSet : %d"),
pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (hddstactx->hdd_ReassocScenario) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Roaming in progress, so unable to proceed this request"));
return -EBUSY;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_get_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("max attribute not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request Id Not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Req Mask Not present"));
return -EINVAL;
}
LinkLayerStatsGetReq.reqId =
nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]);
LinkLayerStatsGetReq.paramIdMask =
nla_get_u32(tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]);
LinkLayerStatsGetReq.staId = pAdapter->sessionId;
spin_lock(&hdd_context_lock);
context = &pHddCtx->ll_stats_context;
context->request_id = LinkLayerStatsGetReq.reqId;
context->request_bitmap = LinkLayerStatsGetReq.paramIdMask;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
if (eHAL_STATUS_SUCCESS != sme_LLStatsGetReq(pHddCtx->hHal,
&LinkLayerStatsGetReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"sme_LLStatsGetReq Failed", __func__);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS));
if (!rc) {
hddLog(LOGE,
FL("Target response timed out request id %d request bitmap 0x%x"),
context->request_id, context->request_bitmap);
return -ETIMEDOUT;
}
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_ll_stats_get() - get ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
static int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_get(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8 },
};
static int __wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1];
tSirLLStatsClearReq LinkLayerStatsClearReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
u32 statsClearReqMask;
u8 stopReq;
int status;
struct sk_buff *temp_skbuff;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (!pAdapter->isLinkLayerStatsSet)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: isLinkLayerStatsSet : %d",
__func__, pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_clr_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("STATS_CLR_MAX is not present"));
return -EINVAL;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] ||
!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Error in LL_STATS CLR CONFIG PARA"));
return -EINVAL;
}
statsClearReqMask = LinkLayerStatsClearReq.statsClearReqMask =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]);
stopReq = LinkLayerStatsClearReq.stopReq =
nla_get_u8(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]);
/*
* Shall take the request Id if the Upper layers pass. 1 For now.
*/
LinkLayerStatsClearReq.reqId = 1;
LinkLayerStatsClearReq.staId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR reqId = %d, staId = %d, statsClearReqMask = 0x%X, stopReq = %d",
LinkLayerStatsClearReq.reqId,
LinkLayerStatsClearReq.staId,
LinkLayerStatsClearReq.statsClearReqMask,
LinkLayerStatsClearReq.stopReq);
if (eHAL_STATUS_SUCCESS == sme_LLStatsClearReq(pHddCtx->hHal,
&LinkLayerStatsClearReq))
{
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
2 * sizeof(u32) +
2 * NLMSG_HDRLEN);
if (temp_skbuff != NULL)
{
if (nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK,
statsClearReqMask) ||
nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP,
stopReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("LL_STATS_CLR put fail"));
kfree_skb(temp_skbuff);
return -EINVAL;
}
/* If the ask is to stop the stats collection as part of clear
* (stopReq = 1) , ensure that no further requests of get
* go to the firmware by having isLinkLayerStatsSet set to 0.
* However it the stopReq as part of the clear request is 0 ,
* the request to get the statistics are honoured as in this
* case the firmware is just asked to clear the statistics.
*/
if (stopReq == 1)
pAdapter->isLinkLayerStatsSet = 0;
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
EXIT();
return -ENOMEM;
}
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_ll_stats_clear() - clear ll stats
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wdev
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 if success, non-zero for failure
*/
static int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_clear(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_clear_link_layer_stats() - clear link layer stats
* @adapter: pointer to adapter
*
* Wrapper function to clear link layer stats.
* return - void
*/
void wlan_hdd_clear_link_layer_stats(hdd_adapter_t *adapter)
{
tSirLLStatsClearReq link_layer_stats_clear_req;
tHalHandle hal = WLAN_HDD_GET_HAL_CTX(adapter);
link_layer_stats_clear_req.statsClearReqMask = WIFI_STATS_IFACE_AC |
WIFI_STATS_IFACE_ALL_PEER;
link_layer_stats_clear_req.stopReq = 0;
link_layer_stats_clear_req.reqId = 1;
link_layer_stats_clear_req.staId = adapter->sessionId;
sme_LLStatsClearReq(hal, &link_layer_stats_clear_req);
return;
}
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
/**
* __wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the Key data
* @data_len:Length of the data passed
*
* This is called when wlan driver needs to save the keys received via
* vendor specific command.
*
* Return: Return the Success or Failure code.
*/
static int __wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
uint8_t local_pmk[SIR_ROAM_SCAN_PSK_SIZE];
struct net_device *dev = wdev->netdev;
hdd_adapter_t *hdd_adapter_ptr = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx_ptr;
int status;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if ((data == NULL) || (data_len == 0) ||
(data_len > SIR_ROAM_SCAN_PSK_SIZE)) {
hddLog(LOGE, FL("Invalid data"));
return -EINVAL;
}
hdd_ctx_ptr = WLAN_HDD_GET_CTX(hdd_adapter_ptr);
if (!hdd_ctx_ptr) {
hddLog(LOGE, FL("HDD context is null"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx_ptr);
if (0 != status)
return status;
sme_UpdateRoamKeyMgmtOffloadEnabled(hdd_ctx_ptr->hHal,
hdd_adapter_ptr->sessionId,
TRUE);
vos_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE);
vos_mem_copy(local_pmk, data, data_len);
sme_RoamSetPSK_PMK(WLAN_HDD_GET_HAL_CTX(hdd_adapter_ptr),
hdd_adapter_ptr->sessionId, local_pmk, data_len);
return 0;
}
/**
* wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the Key data
* @data_len:Length of the data passed
*
* This is called when wlan driver needs to save the keys received via
* vendor specific command.
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_keymgmt_set_key(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_send_roam_auth_event() - Send the roamed and authorized event
* @hdd_ctx_ptr: pointer to HDD Context.
* @bssid: pointer to bssid of roamed AP.
* @req_rsn_ie: Pointer to request RSN IE
* @req_rsn_len: Length of the request RSN IE
* @rsp_rsn_ie: Pointer to response RSN IE
* @rsp_rsn_len: Length of the response RSN IE
* @roam_info_ptr: Pointer to the roaming related information
*
* This is called when wlan driver needs to send the roaming and
* authorization information after roaming.
*
* The information that would be sent is the request RSN IE, response
* RSN IE and BSSID of the newly roamed AP.
*
* If the Authorized status is authenticated, then additional parameters
* like PTK's KCK and KEK and Replay Counter would also be passed to the
* supplicant.
*
* The supplicant upon receiving this event would ignore the legacy
* cfg80211_roamed call and use the entire information from this event.
* The cfg80211_roamed should still co-exist since the kernel will
* make use of the parameters even if the supplicant ignores it.
*
* Return: Return the Success or Failure code.
*/
int wlan_hdd_send_roam_auth_event(hdd_context_t *hdd_ctx_ptr, uint8_t *bssid,
uint8_t *req_rsn_ie, uint32_t req_rsn_len,
uint8_t *rsp_rsn_ie, uint32_t rsp_rsn_len,
tCsrRoamInfo *roam_info_ptr)
{
struct sk_buff *skb = NULL;
ENTER();
if (wlan_hdd_validate_context(hdd_ctx_ptr))
return -EINVAL;
skb = cfg80211_vendor_event_alloc(hdd_ctx_ptr->wiphy,
NULL,
ETH_ALEN + req_rsn_len + rsp_rsn_len +
sizeof(uint8) + SIR_REPLAY_CTR_LEN +
SIR_KCK_KEY_LEN + SIR_KCK_KEY_LEN +
(7 * NLMSG_HDRLEN),
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return -EINVAL;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
ETH_ALEN, bssid) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
req_rsn_len, req_rsn_ie) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
rsp_rsn_len, rsp_rsn_ie)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Auth Status = %d"),
roam_info_ptr->synchAuthStatus);
if (roam_info_ptr->synchAuthStatus ==
CSR_ROAM_AUTH_STATUS_AUTHENTICATED) {
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Include Auth Params TLV's"));
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
TRUE) ||
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
SIR_REPLAY_CTR_LEN, roam_info_ptr->replay_ctr)
|| nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
SIR_KCK_KEY_LEN, roam_info_ptr->kck)
|| nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
SIR_KEK_KEY_LEN, roam_info_ptr->kek)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
} else {
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("No Auth Params TLV's"));
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
FALSE)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
}
cfg80211_vendor_event(skb, GFP_KERNEL);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
#endif
static const struct
nla_policy
qca_wlan_vendor_get_wifi_info_policy[
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX +1] = {
[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX] = {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send wifi driver related info
* (driver/fw version) to the user space application upon request.
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1];
tSirVersionString driver_version;
tSirVersionString firmware_version;
uint32_t major_spid = 0, minor_spid = 0, siid = 0, crmid = 0;
int status;
struct sk_buff *reply_skb;
uint32_t skb_len = 0, count = 0;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, data,
data_len, qca_wlan_vendor_get_wifi_info_policy)) {
hddLog(LOGE, FL("WIFI_INFO_GET NL CMD parsing failed"));
return -EINVAL;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
hddLog(LOG1, FL("Rcvd req for Driver version"));
strlcpy(driver_version, QWLAN_VERSIONSTR,
sizeof(driver_version));
skb_len += strlen(driver_version) + 1;
count++;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
hddLog(LOG1, FL("Rcvd req for FW version"));
hdd_get_fw_version(hdd_ctx, &major_spid, &minor_spid, &siid,
&crmid);
snprintf(firmware_version, sizeof(firmware_version),
"%d:%d:%d:%d", major_spid, minor_spid, siid, crmid);
skb_len += strlen(firmware_version) + 1;
count++;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) {
hddLog(LOG1, FL("Rcvd req for Radio Index"));
skb_len += sizeof(hdd_ctx->radio_index);
count++;
}
if (count == 0) {
hddLog(LOGE, FL("unknown attribute in get_wifi_info request"));
return -EINVAL;
}
skb_len += (NLA_HDRLEN * count) + NLMSG_HDRLEN;
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
if (!reply_skb)
goto error_skb_fail;
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
if (nla_put_string(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION,
driver_version))
goto error_nla_fail;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
if (nla_put_string(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION,
firmware_version))
goto error_nla_fail;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX,
hdd_ctx->radio_index))
goto error_nla_fail;
}
return cfg80211_vendor_cmd_reply(reply_skb);
error_skb_fail:
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
error_nla_fail:
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send wifi driver related info
* (driver/fw version) to the user space application upon request.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_wifi_info(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int status;
uint32_t features;
struct sk_buff *reply_skb = NULL;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
features = 0;
features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED;
features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
features |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN);
if (!reply_skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, FL("Supported logger features: 0x%0x"), features);
if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED,
features)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_WLAN_TDLS
/* EXT TDLS */
static const struct nla_policy
wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] =
{.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type = NLA_S32 },
};
static const struct nla_policy
wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
};
static const struct nla_policy
wlan_hdd_tdls_config_state_change_policy[
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS] =
{.type = NLA_U32 },
};
static const struct nla_policy
wlan_hdd_tdls_config_get_status_policy[
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS]
= {.type = NLA_U32 },
};
static int __wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
uint8_t peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1];
eHalStatus ret;
tANI_U32 state;
tANI_S32 reason;
uint32_t global_operating_class = 0;
uint32_t channel = 0;
struct sk_buff *skb = NULL;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return -EINVAL;
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX,
data, data_len,
wlan_hdd_tdls_config_get_status_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid attribute"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
ret = wlan_hdd_tdls_get_status(pAdapter, peer, &global_operating_class,
&channel, &state, &reason);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("get status Failed"));
return -EINVAL;
}
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
4 * sizeof(int32_t) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Reason %d Status %d class %d channel %d peer " MAC_ADDRESS_STR),
reason, state, global_operating_class,
channel, MAC_ADDR_ARRAY(peer));
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE,
state) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON,
reason) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS,
global_operating_class) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL,
channel)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
ret = cfg80211_vendor_cmd_reply(skb);
EXIT();
return ret;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_exttdls_get_status() - get ext tdls status
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_get_status(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int wlan_hdd_cfg80211_exttdls_callback(const tANI_U8* mac,
uint32_t global_operating_class,
uint32_t channel,
tANI_U32 state,
tANI_S32 reason,
void *ctx)
{
hdd_adapter_t* pAdapter = (hdd_adapter_t*)ctx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
struct sk_buff *skb = NULL;
ENTER();
if (wlan_hdd_validate_context(pHddCtx))
return -EINVAL;
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
return -ENOTSUPP;
}
skb = cfg80211_vendor_event_alloc(
pHddCtx->wiphy,
NULL,
EXTTDLS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Reason %d Status %d class %d channel %d peer " MAC_ADDRESS_STR),
reason, state, global_operating_class,
channel, MAC_ADDR_ARRAY(mac));
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR,
VOS_MAC_ADDR_SIZE, mac) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE,
state) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON,
reason) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL,
channel) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS,
global_operating_class)
) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return (0);
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
static int __wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
uint8_t peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + 1];
eHalStatus status;
tdls_req_params_t pReqMsg = {0};
int ret;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("TDLS External Control is not enabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX,
data, data_len,
wlan_hdd_tdls_config_enable_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
/* Parse and fetch channel */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr channel failed"));
return -EINVAL;
}
pReqMsg.channel = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Channel Num (%d)"), pReqMsg.channel);
/* Parse and fetch global operating class */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr operating class failed"));
return -EINVAL;
}
pReqMsg.global_operating_class = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Operating class (%d)"),
pReqMsg.global_operating_class);
/* Parse and fetch latency ms */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr latency failed"));
return -EINVAL;
}
pReqMsg.max_latency_ms = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Latency (%d)"),
pReqMsg.max_latency_ms);
/* Parse and fetch required bandwidth kbps */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bandwidth failed"));
return -EINVAL;
}
pReqMsg.min_bandwidth_kbps = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Bandwidth (%d)"),
pReqMsg.min_bandwidth_kbps);
ret = wlan_hdd_tdls_extctrl_config_peer(pAdapter,
peer,
wlan_hdd_cfg80211_exttdls_callback,
pReqMsg.channel,
pReqMsg.max_latency_ms,
pReqMsg.global_operating_class,
pReqMsg.min_bandwidth_kbps);
EXIT();
return ret;
}
/**
* wlan_hdd_cfg80211_exttdls_enable() - enable ext tdls
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_enable(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u8 peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + 1];
eHalStatus status;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -EINVAL;
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX,
data, data_len,
wlan_hdd_tdls_config_disable_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
status = wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
EXIT();
return status;
}
/**
* wlan_hdd_cfg80211_exttdls_disable() - disable ext tdls
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_disable(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
static const struct nla_policy
wlan_hdd_set_no_dfs_flag_config_policy[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX
+1] =
{
[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG] = {.type = NLA_U32 },
};
/**
* wlan_hdd_disable_dfs_chan_scan () - disable/enable DFS channels
*
* @pHddCtx: HDD context within host driver
* @pAdapter: Adapter pointer
* @no_dfs_flag: If TRUE, DFS channels cannot be used for scanning
*
* Loops through devices to see who is operating on DFS channels
* and then disables/enables DFS channels by calling SME API.
* Fails the disable request if any device is active on a DFS channel.
*
* Return: EOK or other error codes.
*/
int wlan_hdd_disable_dfs_chan_scan(hdd_context_t *pHddCtx,
hdd_adapter_t *pAdapter,
u32 no_dfs_flag)
{
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
hdd_adapter_list_node_t *p_adapter_node = NULL, *p_next = NULL;
hdd_adapter_t *p_adapter;
VOS_STATUS vos_status;
hdd_ap_ctx_t *p_ap_ctx;
hdd_station_ctx_t *p_sta_ctx;
eHalStatus status;
int ret_val = -EPERM;
if (no_dfs_flag == pHddCtx->cfg_ini->enableDFSChnlScan) {
if (no_dfs_flag) {
vos_status = hdd_get_front_adapter( pHddCtx, &p_adapter_node);
while ((NULL != p_adapter_node) &&
(VOS_STATUS_SUCCESS == vos_status))
{
p_adapter = p_adapter_node->pAdapter;
if (WLAN_HDD_SOFTAP == p_adapter->device_mode) {
p_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(p_adapter);
/* if there is SAP already running on DFS channel,
do not disable scan on dfs channels. Note that with
SAP on DFS, there cannot be conurrency on single
radio. But then we can have multiple radios !!!!! */
if (NV_CHANNEL_DFS ==
vos_nv_getChannelEnabledState(
p_ap_ctx->operatingChannel)) {
hddLog(LOGE, FL("SAP running on DFS channel"));
return -EOPNOTSUPP;
}
}
if (WLAN_HDD_INFRA_STATION == p_adapter->device_mode) {
p_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(p_adapter);
/* if STA is already connected on DFS channel,
do not disable scan on dfs channels */
if (hdd_connIsConnected(p_sta_ctx) &&
(NV_CHANNEL_DFS ==
vos_nv_getChannelEnabledState(
p_sta_ctx->conn_info.operationChannel))) {
hddLog(LOGE, FL("client connected on DFS channel"));
return -EOPNOTSUPP;
}
}
vos_status = hdd_get_next_adapter(pHddCtx, p_adapter_node,
&p_next);
p_adapter_node = p_next;
}
}
pHddCtx->cfg_ini->enableDFSChnlScan = !no_dfs_flag;
hdd_abort_mac_scan_all_adapters(pHddCtx);
/* call the SME API to tunnel down the new channel list
to the firmware */
status = sme_handle_dfs_chan_scan(hHal,
pHddCtx->cfg_ini->enableDFSChnlScan);
if (eHAL_STATUS_SUCCESS == status) {
ret_val = 0;
/* Clear the SME scan cache also. Note that the clearing of scan
* results is independent of session; so no need to iterate over
* all sessions
*/
status = sme_ScanFlushResult(hHal, pAdapter->sessionId);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EPERM;
}
} else {
hddLog(LOG1, FL(" the DFS flag has not changed"));
ret_val = 0;
}
return ret_val;
}
/**
* __wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendof command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX. Validate it and
* call wlan_hdd_disable_dfs_chan_scan to send it to firmware.
*
* Return: EOK or other error codes.
*/
static int __wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1];
int ret_val = -EPERM;
u32 no_dfs_flag = 0;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if ((ret_val = wlan_hdd_validate_context(pHddCtx)))
return ret_val;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX,
data, data_len,
wlan_hdd_set_no_dfs_flag_config_policy)) {
hddLog(LOGE, FL("invalid attr"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) {
hddLog(LOGE, FL("attr dfs flag failed"));
return -EINVAL;
}
no_dfs_flag = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]);
hddLog(LOG1, FL(" DFS flag = %d"),
no_dfs_flag);
if (no_dfs_flag > 1) {
hddLog(LOGE, FL("invalid value of dfs flag"));
return -EINVAL;
}
ret_val = wlan_hdd_disable_dfs_chan_scan(pHddCtx, pAdapter, no_dfs_flag);
return ret_val;
}
/**
* wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendof command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX. Validate it and
* call wlan_hdd_disable_dfs_chan_scan to send it to firmware.
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_disable_dfs_chan_scan(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_sap_cfg_dfs_override() - DFS MCC restriction check
*
* @adapter: SAP adapter pointer
*
* DFS in MCC is not supported for Multi bssid SAP mode due to single physical
* radio. So in case of DFS MCC scenario override current SAP given config
* to follow concurrent SAP DFS config
*
* Return: 0 - No DFS issue, 1 - Override done and negative error codes
*/
#ifdef WLAN_FEATURE_MBSSID
static int wlan_hdd_sap_cfg_dfs_override(hdd_adapter_t *adapter)
{
hdd_adapter_t *con_sap_adapter;
tsap_Config_t *sap_config, *con_sap_config;
int con_ch;
tHalHandle hHal;
/*
* Check if STA is running in a concurrent channel
*/
hHal = WLAN_HDD_GET_HAL_CTX(adapter);
con_ch = sme_GetConcurrentOperationChannel(hHal);
sap_config = &adapter->sessionCtx.ap.sapConfig;
if (con_ch && vos_get_concurrency_mode() == VOS_STA_SAP) {
hddLog(LOG1, FL("concurrent STA role running on channel %d"),
con_ch);
/*
* There is a STA role running on the same card, in that case
* DFS channel cannot be used by concurrent SAP.
* Try to use the same channel as the STA to achieve SCC
*/
if (VOS_IS_DFS_CH(sap_config->channel)) {
hddLog(LOG1,
FL("SAP channel config overridden due to DFS channel not allowed in STA+SAP mode %d -> %d"),
sap_config->channel, con_ch);
sap_config->channel = con_ch;
}
}
/*
* Check if AP+AP case, once primary AP chooses a DFS
* channel secondary AP should always follow primary APs channel
*/
if (!vos_concurrent_beaconing_sessions_running())
return 0;
con_sap_adapter = hdd_get_con_sap_adapter(adapter, true);
if (!con_sap_adapter)
return 0;
con_sap_config = &con_sap_adapter->sessionCtx.ap.sapConfig;
con_ch = con_sap_adapter->sessionCtx.ap.operatingChannel;
if (!VOS_IS_DFS_CH(con_ch))
return 0;
hddLog(LOGE, FL("Only SCC AP-AP DFS Permitted (ch=%d, con_ch=%d)"),
sap_config->channel, con_ch);
hddLog(LOG1, FL("Overriding guest AP's channel"));
sap_config->channel = con_ch;
if (con_sap_config->acs_cfg.acs_mode == true) {
if (con_ch != con_sap_config->acs_cfg.pri_ch &&
con_ch != con_sap_config->acs_cfg.ht_sec_ch) {
hddLog(LOGE, FL("Primary AP channel config error"));
hddLog(LOGE, FL("Operating ch: %d ACS ch: %d %d"),
con_ch, con_sap_config->acs_cfg.pri_ch,
con_sap_config->acs_cfg.ht_sec_ch);
return -EINVAL;
}
/* Sec AP ACS info is overwritten with Pri AP due to DFS
* MCC restriction. So free ch list allocated in do_acs
* func for Sec AP and realloc for Pri AP ch list size
*/
if (sap_config->acs_cfg.ch_list)
vos_mem_free(sap_config->acs_cfg.ch_list);
vos_mem_copy(&sap_config->acs_cfg,
&con_sap_config->acs_cfg,
sizeof(struct sap_acs_cfg));
sap_config->acs_cfg.ch_list = vos_mem_malloc(
sizeof(uint8_t) *
con_sap_config->acs_cfg.ch_list_count);
if (!sap_config->acs_cfg.ch_list) {
hddLog(LOGE, FL("ACS config alloc fail"));
return -ENOMEM;
}
vos_mem_copy(sap_config->acs_cfg.ch_list,
con_sap_config->acs_cfg.ch_list,
con_sap_config->acs_cfg.ch_list_count);
} else {
sap_config->acs_cfg.pri_ch = con_ch;
if (sap_config->acs_cfg.ch_width > eHT_CHANNEL_WIDTH_20MHZ)
sap_config->acs_cfg.ht_sec_ch = con_sap_config->sec_ch;
}
return con_ch;
}
#else
static int wlan_hdd_sap_cfg_dfs_override(hdd_adapter_t *adapter)
{
return 0;
}
#endif
static int wlan_hdd_config_acs(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter)
{
tsap_Config_t *sap_config;
hdd_config_t *ini_config;
tHalHandle hal;
hal = WLAN_HDD_GET_HAL_CTX(adapter);
sap_config = &adapter->sessionCtx.ap.sapConfig;
ini_config = hdd_ctx->cfg_ini;
sap_config->enOverLapCh = !!hdd_ctx->cfg_ini->gEnableOverLapCh;
#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
hddLog(LOG1, FL("HDD_ACS_SKIP_STATUS = %d"), hdd_ctx->skip_acs_scan_status);
if (hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) {
hdd_adapter_t *con_sap_adapter;
tsap_Config_t *con_sap_config = NULL;
con_sap_adapter = hdd_get_con_sap_adapter(adapter, false);
if (con_sap_adapter)
con_sap_config = &con_sap_adapter->sessionCtx.ap.sapConfig;
sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN;
if (con_sap_config && con_sap_config->acs_cfg.acs_mode == true &&
hdd_ctx->skip_acs_scan_status == eSAP_SKIP_ACS_SCAN) {
if (con_sap_config->acs_cfg.hw_mode == sap_config->acs_cfg.hw_mode) {
v_U8_t con_sap_st_ch, con_sap_end_ch;
v_U8_t cur_sap_st_ch, cur_sap_end_ch;
v_U8_t bandStartChannel, bandEndChannel;
con_sap_st_ch = con_sap_config->acs_cfg.start_ch;
con_sap_end_ch = con_sap_config->acs_cfg.end_ch;
cur_sap_st_ch = sap_config->acs_cfg.start_ch;
cur_sap_end_ch = sap_config->acs_cfg.end_ch;
WLANSAP_extend_to_acs_range(&cur_sap_st_ch, &cur_sap_end_ch,
&bandStartChannel, &bandEndChannel);
WLANSAP_extend_to_acs_range(&con_sap_st_ch, &con_sap_end_ch,
&bandStartChannel, &bandEndChannel);
if (con_sap_st_ch <= cur_sap_st_ch &&
con_sap_end_ch >= cur_sap_end_ch) {
sap_config->acs_cfg.skip_scan_status = eSAP_SKIP_ACS_SCAN;
} else if (con_sap_st_ch >= cur_sap_st_ch &&
con_sap_end_ch >= cur_sap_end_ch) {
sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN;
sap_config->acs_cfg.skip_scan_range1_stch = cur_sap_st_ch;
sap_config->acs_cfg.skip_scan_range1_endch =
con_sap_st_ch - 1;
sap_config->acs_cfg.skip_scan_range2_stch = 0;
sap_config->acs_cfg.skip_scan_range2_endch = 0;
} else if (con_sap_st_ch <= cur_sap_st_ch &&
con_sap_end_ch <= cur_sap_end_ch) {
sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN;
sap_config->acs_cfg.skip_scan_range1_stch =
con_sap_end_ch + 1;
sap_config->acs_cfg.skip_scan_range1_endch = cur_sap_end_ch;
sap_config->acs_cfg.skip_scan_range2_stch = 0;
sap_config->acs_cfg.skip_scan_range2_endch = 0;
} else if (con_sap_st_ch >= cur_sap_st_ch &&
con_sap_end_ch <= cur_sap_end_ch) {
sap_config->acs_cfg.skip_scan_status = eSAP_DO_PAR_ACS_SCAN;
sap_config->acs_cfg.skip_scan_range1_stch = cur_sap_st_ch;
sap_config->acs_cfg.skip_scan_range1_endch =
con_sap_st_ch - 1;
sap_config->acs_cfg.skip_scan_range2_stch = con_sap_end_ch;
sap_config->acs_cfg.skip_scan_range2_endch =
cur_sap_end_ch + 1;
} else
sap_config->acs_cfg.skip_scan_status = eSAP_DO_NEW_ACS_SCAN;
}
hddLog(LOG1,
FL("SecAP ACS Skip = %d, ACS CH RANGE = %d-%d, %d-%d"),
sap_config->acs_cfg.skip_scan_status,
sap_config->acs_cfg.skip_scan_range1_stch,
sap_config->acs_cfg.skip_scan_range1_endch,
sap_config->acs_cfg.skip_scan_range2_stch,
sap_config->acs_cfg.skip_scan_range2_endch);
}
}
#endif
return 0;
}
/**
* wlan_hdd_set_acs_ch_range : Start ACS channel range values
* @sap_cfg: pointer to SAP config struct
*
* This function sets the default ACS start and end channel for the given band
* and also parses the given ACS channel list.
*
* Return: None
*/
static void wlan_hdd_set_acs_ch_range(tsap_Config_t *sap_cfg, bool ht_enabled,
bool vht_enabled)
{
int i;
if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211B) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11b;
sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_1].channelNum;
sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_14].channelNum;
sap_cfg->target_band = eCSR_BAND_24;
} else if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211G) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11g;
sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_1].channelNum;
sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_13].channelNum;
sap_cfg->target_band = eCSR_BAND_24;
} else if (sap_cfg->acs_cfg.hw_mode == QCA_ACS_MODE_IEEE80211A) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11a;
sap_cfg->acs_cfg.start_ch = rfChannels[RF_CHAN_36].channelNum;
sap_cfg->acs_cfg.end_ch = rfChannels[RF_CHAN_165].channelNum;
sap_cfg->target_band = eCSR_BAND_5G;
} else {
hddLog(LOG1, FL("hw_mode %d"), sap_cfg->acs_cfg.hw_mode);
sap_cfg->target_band = eCSR_BAND_5G;
}
if (ht_enabled)
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11n;
if (vht_enabled)
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac;
/* Parse ACS Chan list from hostapd */
if (!sap_cfg->acs_cfg.ch_list)
return;
sap_cfg->acs_cfg.start_ch = sap_cfg->acs_cfg.ch_list[0];
sap_cfg->acs_cfg.end_ch =
sap_cfg->acs_cfg.ch_list[sap_cfg->acs_cfg.ch_list_count - 1];
for (i = 0; i < sap_cfg->acs_cfg.ch_list_count; i++) {
/* avoid channel 0 as start channel */
if (sap_cfg->acs_cfg.start_ch > sap_cfg->acs_cfg.ch_list[i] &&
sap_cfg->acs_cfg.ch_list[i] != 0 )
sap_cfg->acs_cfg.start_ch = sap_cfg->acs_cfg.ch_list[i];
if (sap_cfg->acs_cfg.end_ch < sap_cfg->acs_cfg.ch_list[i])
sap_cfg->acs_cfg.end_ch = sap_cfg->acs_cfg.ch_list[i];
}
}
static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work);
/**
* wlan_hdd_cfg80211_start_acs : Start ACS Procedure for SAP
* @adapter: pointer to SAP adapter struct
*
* This function starts the ACS procedure if there are no
* constraints like MBSSID DFS restrictions.
*
* Return: Status of ACS Start procedure
*/
static int wlan_hdd_cfg80211_start_acs(hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
tsap_Config_t *sap_config;
tpWLAN_SAPEventCB acs_event_callback;
int status;
sap_config = &adapter->sessionCtx.ap.sapConfig;
if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))
sap_config->backup_channel = sap_config->channel;
if (hdd_ctx->acs_policy.acs_channel)
sap_config->channel = hdd_ctx->acs_policy.acs_channel;
else
sap_config->channel = AUTO_CHANNEL_SELECT;
status = wlan_hdd_sap_cfg_dfs_override(adapter);
if (status < 0) {
return status;
} else {
if (status > 0) {
/* notify hostapd about channel override */
wlan_hdd_cfg80211_acs_ch_select_evt(adapter);
clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return 0;
}
}
status = wlan_hdd_config_acs(hdd_ctx, adapter);
if (status) {
hddLog(LOGE, FL("ACS config failed"));
return -EINVAL;
}
acs_event_callback = hdd_hostapd_SAPEventCB;
vos_mem_copy(sap_config->self_macaddr.bytes,
adapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
hddLog(LOG1, FL("ACS Started for wlan%d"), adapter->dev->ifindex);
status = WLANSAP_ACS_CHSelect(
#ifdef WLAN_FEATURE_MBSSID
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
#else
hdd_ctx->pvosContext,
#endif
acs_event_callback, sap_config, (v_PVOID_t)adapter->dev);
if (status) {
hddLog(LOGE, FL("ACS channel select failed"));
return -EINVAL;
}
sap_config->acs_cfg.acs_mode = true;
#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
if (is_auto_channel_select(WLAN_HDD_GET_SAP_CTX_PTR(adapter)))
#endif
set_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return 0;
}
/**
* wlan_hdd_cfg80211_relaunch_acs() - Relaunch ACS for SAP
* @adapter: pointer to SAP adapter structure
*
* This function relaunches ACS.
*
* Return: Result status of ACS relaunch
*/
static int wlan_hdd_cfg80211_relaunch_acs(hdd_adapter_t *adapter)
{
uint8_t channel_list[MAX_CHANNEL] = {0};
uint8_t number_of_channels = 0;
hdd_context_t *hdd_ctx = (hdd_context_t *)adapter->pHddCtx;
tsap_Config_t *sap_config;
int status;
uint8_t cur_band, i;
uint8_t band_start_channel;
uint8_t band_end_channel;
bool ht_enabled = false, vht_enabled = false;
if (!test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) {
hddLog(LOGE, FL("Softap is not started"));
return -EINVAL;
}
cur_band = vos_chan_to_band((WLAN_HDD_GET_AP_CTX_PTR(
adapter))->operatingChannel);
sap_config = &adapter->sessionCtx.ap.sapConfig;
vos_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg));
if (VOS_BAND_2GHZ == cur_band) {
band_start_channel = RF_CHAN_1;
band_end_channel = RF_CHAN_14;
} else {
band_start_channel = RF_CHAN_36;
band_end_channel = RF_CHAN_165;
}
for (i = band_start_channel; i <= band_end_channel; i++) {
if (NV_CHANNEL_ENABLE ==
vos_nv_getChannelEnabledState(
rfChannels[i].channelNum)) {
channel_list[number_of_channels++] =
rfChannels[i].channelNum;
hddLog(LOG1,
FL("Config acs channel[%d]"),
rfChannels[i].channelNum);
}
}
if (sap_config->SapHw_mode &
(eCSR_DOT11_MODE_11n |
eCSR_DOT11_MODE_11n_ONLY |
eCSR_DOT11_MODE_11ac))
ht_enabled = true;
if (sap_config->SapHw_mode &
(eCSR_DOT11_MODE_11ac |
eCSR_DOT11_MODE_11ac_ONLY))
vht_enabled = true;
sap_config->acs_cfg.ch_width = sap_config->ch_width_orig;
hddLog(LOG1,
FL("ht_enabled=%d vht_enabled=%d ch_width=%d"),
ht_enabled,
vht_enabled,
sap_config->acs_cfg.ch_width);
wlan_hdd_set_acs_ch_range(sap_config, ht_enabled, vht_enabled);
sap_config->acsBandSwitchThreshold =
hdd_ctx->cfg_ini->acsBandSwitchThreshold;
if (hdd_ctx->cfg_ini->auto_channel_select_weight)
sap_config->auto_channel_select_weight =
hdd_ctx->cfg_ini->auto_channel_select_weight;
sap_config->acs_cfg.acs_mode = true;
if (test_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags)) {
hddLog(LOG1, FL("ACS Pending for wlan"));
status = -EINVAL;
} else {
hddLog(LOG1, FL("Relaunch ACS"));
wlan_hdd_cfg80211_start_acs(adapter);
status = 0;
}
return status;
}
#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
/**
* wlan_hdd_set_mcc_to_scc_switch() - set mcc to scc switch mode from ini
* @adapter: hdd_adapter_t ptr of the interface
*
* This function updates ini WlanMccToSccSwitchMode value to adapter
* context. The value controls the MCC to SCC switch behavior.
*
* Return: void
*/
static void
wlan_hdd_set_mcc_to_scc_switch(hdd_adapter_t *adapter)
{
hdd_context_t * hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_config_t *cfg_ini = hdd_ctx->cfg_ini;
tsap_Config_t *sap_config;
sap_config = &adapter->sessionCtx.ap.sapConfig;
sap_config->cc_switch_mode = cfg_ini->WlanMccToSccSwitchMode;
sap_config->band_switch_enable = cfg_ini->wlan_band_switch_enable;
sap_config->ap_p2pclient_concur_enable =
cfg_ini->wlan_ap_p2pgo_conc_enable;
sap_config->ch_width_24g_orig = cfg_ini->nChannelBondingMode24GHz ?
eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ;
sap_config->ch_width_5g_orig = cfg_ini->vhtChannelWidth;
}
#else
static void
wlan_hdd_set_mcc_to_scc_switch(hdd_adapter_t *adapter)
{}
#endif
static const struct nla_policy
wlan_hdd_cfg80211_do_acs_policy[QCA_WLAN_VENDOR_ATTR_ACS_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST] = { .type = NLA_UNSPEC },
};
/**
* __wlan_hdd_cfg80211_do_acs() : CFG80211 handler fucntion for DO_ACS
* Vendor CMD
* @wiphy: Linux wiphy struct pointer
* @wdev: Linux wireless device struct pointer
* @data: ACS information from hostapd
* @data_len: ACS information len
*
* This function handle DO_ACS Vendor command from hostapd, parses ACS config
* and starts ACS procedure.
*
* Return: ACS procedure start status
*/
static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *ndev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
tsap_Config_t *sap_config;
struct sk_buff *temp_skbuff;
int status = -EINVAL, i = 0;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
bool ht_enabled, ht40_enabled, vht_enabled;
uint8_t ch_width;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (hdd_ctx->cfg_ini->force_sap_acs) {
hddLog(LOGE, FL("Hostapd ACS rejected as driver INI force ACS is enabled"));
return -EPERM;
}
/* ***Note*** Donot set SME config related to ACS operation here because
* ACS operation is not synchronouse and ACS for Second AP may come when
* ACS operation for first AP is going on. So only do_acs is split to
* seperate start_acs routine. Also SME-PMAC struct that is used to
* pass paremeters from HDD to SAP is global. Thus All ACS related SME
* config shall be set only from start_acs.
*/
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
if (hdd_cfg_is_static_sub20_channel_width_enabled(hdd_ctx)) {
hddLog(LOGE, FL("ACS not support if static sub20 enable"));
status = -EINVAL;
goto out;
}
if (test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags)) {
uint32_t sap_sub20_channelwidth;
WLANSAP_get_sub20_channelwidth(WLAN_HDD_GET_SAP_CTX_PTR(
adapter),
&sap_sub20_channelwidth);
if (sap_sub20_channelwidth == SUB20_MODE_NONE) {
hddLog(LOGE, FL("Bss started, relaunch ACS"));
status = wlan_hdd_cfg80211_relaunch_acs(adapter);
} else {
hddLog(LOGE, FL("ACS not support in sub20 enable"));
status = -EINVAL;
}
return status;
}
sap_config = &adapter->sessionCtx.ap.sapConfig;
vos_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg));
status = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, data, data_len,
wlan_hdd_cfg80211_do_acs_policy);
if (status) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
goto out;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Attr hw_mode failed"));
goto out;
}
sap_config->acs_cfg.hw_mode = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED])
ht_enabled =
nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED]);
else
ht_enabled = 0;
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED])
ht40_enabled =
nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED]);
else
ht40_enabled = 0;
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED])
vht_enabled =
nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED]);
else
vht_enabled = 0;
if (hdd_ctx->cfg_ini->sap_force_11n_for_11ac) {
vht_enabled = 0;
hddLog(LOG1, FL("VHT is Disabled in ACS"));
}
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) {
ch_width = nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
} else {
if (ht_enabled && ht40_enabled)
ch_width = 40;
else
ch_width = 20;
}
/* this may be possible, when sap_force_11n_for_11ac is set */
if ((ch_width == 80 || ch_width == 160) && !vht_enabled) {
if (ht_enabled && ht40_enabled)
ch_width = 40;
else
ch_width = 20;
}
if (ch_width == 80)
sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_80MHZ;
else if (ch_width == 40)
sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_40MHZ;
else
sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_20MHZ;
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]) {
char *tmp = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]);
sap_config->acs_cfg.ch_list_count = nla_len(
tb[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST]);
if (sap_config->acs_cfg.ch_list_count) {
sap_config->acs_cfg.ch_list = vos_mem_malloc(
sizeof(uint8_t) *
sap_config->acs_cfg.ch_list_count);
if (sap_config->acs_cfg.ch_list == NULL) {
hddLog(LOGE, FL("ACS config alloc fail"));
status = -ENOMEM;
goto out;
}
vos_mem_copy(sap_config->acs_cfg.ch_list, tmp,
sap_config->acs_cfg.ch_list_count);
}
}
wlan_hdd_set_mcc_to_scc_switch(adapter);
wlan_hdd_set_acs_ch_range(sap_config, ht_enabled, vht_enabled);
/* ACS override for android */
if (hdd_ctx->cfg_ini->sap_p2p_11ac_override && ht_enabled &&
!hdd_ctx->cfg_ini->sap_force_11n_for_11ac) {
hddLog(LOG1, FL("ACS Config override for 11AC"));
vht_enabled = 1;
sap_config->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac;
sap_config->acs_cfg.ch_width =
hdd_ctx->cfg_ini->vhtChannelWidth;
/* No VHT80 in 2.4G so perform ACS accordingly */
if (sap_config->acs_cfg.end_ch <= 14 &&
sap_config->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ)
sap_config->acs_cfg.ch_width = eHT_CHANNEL_WIDTH_40MHZ;
}
sap_config->acsBandSwitchThreshold =
hdd_ctx->cfg_ini->acsBandSwitchThreshold;
if (hdd_ctx->cfg_ini->auto_channel_select_weight)
sap_config->auto_channel_select_weight =
hdd_ctx->cfg_ini->auto_channel_select_weight;
hddLog(LOG1, FL("ACS Config for wlan%d: HW_MODE: %d ACS_BW: %d HT: %d VHT: %d START_CH: %d END_CH: %d"),
adapter->dev->ifindex, sap_config->acs_cfg.hw_mode,
ch_width, ht_enabled, vht_enabled,
sap_config->acs_cfg.start_ch, sap_config->acs_cfg.end_ch);
if (sap_config->acs_cfg.ch_list_count) {
hddLog(LOG1, FL("ACS channel list: len: %d"),
sap_config->acs_cfg.ch_list_count);
for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++)
hddLog(LOG1, "%d ", sap_config->acs_cfg.ch_list[i]);
}
sap_config->acs_cfg.acs_mode = true;
if (test_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags)) {
/* ***Note*** Completion variable usage is not allowed here since
* ACS scan operation may take max 2.2 sec for 5G band.
* 9 Active channel X 40 ms active scan time +
* 16 Passive channel X 110ms passive scan time
* Since this CFG80211 call lock rtnl mutex, we cannot hold on
* for this long. So we split up the scanning part.
*/
set_bit(ACS_PENDING, &adapter->event_flags);
hddLog(LOG1, FL("ACS Pending for wlan%d"),
adapter->dev->ifindex);
status = 0;
} else {
status = wlan_hdd_cfg80211_start_acs(adapter);
}
out:
if (0 == status) {
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
NLMSG_HDRLEN);
if (temp_skbuff != NULL)
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
wlan_hdd_undo_acs(adapter);
clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return status;
}
/**
* wlan_hdd_cfg80211_do_acs : CFG80211 handler fucntion for DO_ACS Vendor CMD
* @wiphy: Linux wiphy struct pointer
* @wdev: Linux wireless device struct pointer
* @data: ACS information from hostapd
* @data_len: ACS information len
*
* This function handle DO_ACS Vendor command from hostapd, parses ACS config
* and starts ACS procedure.
*
* Return: ACS procedure start status
*/
static int wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_do_acs(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_undo_acs : Do cleanup of DO_ACS
* @adapter: Pointer to adapter struct
*
* This function handle cleanup of what was done in DO_ACS, including free
* memory.
*
* Return: void
*/
void wlan_hdd_undo_acs(hdd_adapter_t *adapter)
{
if (adapter == NULL)
return;
if (adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list) {
vos_mem_free(adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list);
adapter->sessionCtx.ap.sapConfig.acs_cfg.ch_list = NULL;
}
}
/**
* wlan_hdd_cfg80211_start_pending_acs : Start pending ACS procedure for SAP
* @work: Linux workqueue struct pointer for ACS work
*
* This function starts the ACS procedure which was marked pending when an ACS
* procedure was in progress for a concurrent SAP interface.
*
* Return: None
*/
static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work)
{
hdd_adapter_t *adapter = container_of(work, hdd_adapter_t,
acs_pending_work.work);
wlan_hdd_cfg80211_start_acs(adapter);
}
/**
* wlan_hdd_cfg80211_acs_ch_select_evt: Callback function for ACS evt
* @adapter: Pointer to SAP adapter struct
* @pri_channel: SAP ACS procedure selected Primary channel
* @sec_channel: SAP ACS procedure selected secondary channel
*
* This is a callback function from SAP module on ACS procedure is completed.
* This function send the ACS selected channel information to hostapd
*
* Return: None
*/
void wlan_hdd_cfg80211_acs_ch_select_evt(hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
tsap_Config_t *sap_cfg = &(WLAN_HDD_GET_AP_CTX_PTR(adapter))->sapConfig;
struct sk_buff *vendor_event;
int ret_val;
struct nlattr *nla;
hdd_adapter_t *con_sap_adapter;
uint16_t ch_width;
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL,
4 * sizeof(u8) + 1 * sizeof(u16) + 4 + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return;
}
/* Send the IF INDEX to differentiate the ACS event for each interface
* TODO: To be update once cfg80211 APIs are updated to accept if_index
*/
nla_nest_cancel(vendor_event, ((void **)vendor_event->cb)[2]);
ret_val = nla_put_u32(vendor_event, NL80211_ATTR_IFINDEX,
adapter->dev->ifindex);
if (ret_val) {
hddLog(LOGE, FL("NL80211_ATTR_IFINDEX put fail"));
kfree_skb(vendor_event);
return;
}
nla = nla_nest_start(vendor_event, NL80211_ATTR_VENDOR_DATA);
((void **)vendor_event->cb)[2] = nla;
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
sap_cfg->acs_cfg.pri_ch);
if (ret_val) {
hddLog(LOGE,
FL("QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL put fail"));
kfree_skb(vendor_event);
return;
}
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
sap_cfg->acs_cfg.ht_sec_ch);
if (ret_val) {
hddLog(LOGE,
FL(
"QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL put fail"));
kfree_skb(vendor_event);
return;
}
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
sap_cfg->acs_cfg.vht_seg0_center_ch);
if (ret_val) {
hddLog(LOGE,
FL(
"QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL put fail"));
kfree_skb(vendor_event);
return;
}
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
sap_cfg->acs_cfg.vht_seg1_center_ch);
if (ret_val) {
hddLog(LOGE,
FL(
"QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL put fail"));
kfree_skb(vendor_event);
return;
}
if (sap_cfg->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_80MHZ)
ch_width = 80;
else if (sap_cfg->acs_cfg.ch_width == eHT_CHANNEL_WIDTH_40MHZ)
ch_width = 40;
else
ch_width = 20;
ret_val = nla_put_u16(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
ch_width);
if (ret_val) {
hddLog(LOGE,
FL(
"QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH put fail"));
kfree_skb(vendor_event);
return;
}
hddLog(LOG1,
FL("ACS result for wlan%d: PRI_CH: %d SEC_CH: %d VHT_SEG0: %d VHT_SEG1: %d ACS_BW: %d"),
adapter->dev->ifindex, sap_cfg->acs_cfg.pri_ch,
sap_cfg->acs_cfg.ht_sec_ch,sap_cfg->acs_cfg.vht_seg0_center_ch,
sap_cfg->acs_cfg.vht_seg1_center_ch, ch_width);
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
/* ***Note*** As already mentioned Completion variable usage is not
* allowed here since ACS scan operation may take max 2.2 sec.
* Further in AP-AP mode pending ACS is resumed here to serailize ACS
* operation.
* TODO: Delayed operation is used since SME-PMAC strut is global. Thus
* when Primary AP ACS is complete and secondary AP ACS is started here
* immediately, Primary AP start_bss may come inbetween ACS operation
* and overwrite Sec AP ACS paramters. Thus Sec AP ACS is executed with
* delay. This path and below constraint will be removed on sessionizing
* SAP acs parameters and decoupling SAP from PMAC (WIP).
* As per design constraint user space control application must take
* care of serailizing hostapd start for each VIF in AP-AP mode to avoid
* this code path. Sec AP hostapd should be started after Primary AP
* start beaconing which can be confirmed by getchannel iwpriv command
*/
con_sap_adapter = hdd_get_con_sap_adapter(adapter, false);
if (con_sap_adapter &&
test_bit(ACS_PENDING, &con_sap_adapter->event_flags)) {
#ifdef CONFIG_CNSS
cnss_init_delayed_work(&con_sap_adapter->acs_pending_work,
wlan_hdd_cfg80211_start_pending_acs);
#else
INIT_DELAYED_WORK(&con_sap_adapter->acs_pending_work,
wlan_hdd_cfg80211_start_pending_acs);
#endif
/* Lets give 500ms for OBSS + START_BSS to complete */
schedule_delayed_work(&con_sap_adapter->acs_pending_work,
msecs_to_jiffies(500));
clear_bit(ACS_PENDING, &con_sap_adapter->event_flags);
}
return;
}
static const struct nla_policy
wlan_hdd_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX
+1] =
{
[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR] = {.type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_RATE] = {.type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] = {
.type = NLA_UNSPEC},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY] = {.type = NLA_U32},
};
/**
* wlan_hdd_update_tx_rate() - update tx rate to firmware
* @hdd_ctx: HDD context
* @tx_rate: User-specified tx-rate to be operated for the specific
* HW mode.
* Return: 0 on success; Errno on failure
*/
int wlan_hdd_update_tx_rate(hdd_context_t *hddctx, uint16_t tx_rate)
{
hdd_adapter_t *adapter;
hdd_station_ctx_t *hddstactx;
eHalStatus hstatus;
struct sir_txrate_update *buf_txrate_update;
ENTER();
adapter = hdd_get_adapter(hddctx, WLAN_HDD_INFRA_STATION);
if (!adapter) {
hddLog(LOGE, FL("hdd adapter is null"));
return -EINVAL;
}
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hddLog(LOGE, FL("hdd adapter cookie is invalid"));
return -EINVAL;
}
if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
hddLog(LOGE, FL("Only Sta Mode supported!"));
return -ENOTSUPP;
}
hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (!hdd_connIsConnected(hddstactx)) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
buf_txrate_update = vos_mem_malloc(sizeof(*buf_txrate_update));
if (!buf_txrate_update) {
hddLog(LOGE, FL("Failed to allocate memory for buf_txrate_update"));
return -ENOMEM;
}
buf_txrate_update->session_id = adapter->sessionId;
buf_txrate_update->txrate = tx_rate;
vos_mem_copy(buf_txrate_update->bssid, hddstactx->conn_info.bssId,
VOS_MAC_ADDR_SIZE);
hstatus = sme_update_txrate(hddctx->hHal, buf_txrate_update);
if (!HAL_STATUS_SUCCESS(hstatus)) {
hddLog(LOGE,
FL("sme_update_txrate failed(err=%d)"), hstatus);
vos_mem_free(buf_txrate_update);
return -EINVAL;
}
EXIT();
vos_mem_free(buf_txrate_update);
return 0;
}
/**
* hdd_set_qpower_config() - set qpower config to firmware
* @adapter: HDD adapter
* @qpower: new qpower config value
*
* Return: 0 on success; Errno on failure
*/
static int hdd_set_qpower_config(hdd_context_t *hddctx, hdd_adapter_t *adapter,
uint8_t qpower)
{
VOS_STATUS vos_status;
if(!adapter) {
hddLog(LOGE,"invalid adapter");
return -EINVAL;
}
if (adapter->device_mode != WLAN_HDD_INFRA_STATION &&
adapter->device_mode != WLAN_HDD_P2P_CLIENT) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("QPOWER is only supported for STA/P2P-CLIENT"));
return -EINVAL;
}
if (!hddctx->cfg_ini->enablePowersaveOffload) {
hddLog(LOGE,
FL("qpower is disabled in configuration"));
return -EINVAL;
}
if (qpower > PS_DUTY_CYCLING_QPOWER ||
qpower < PS_LEGACY_NODEEPSLEEP) {
hddLog(LOGE,
FL("invalid qpower value=%d"), qpower);
return -EINVAL;
}
vos_status = wma_set_powersave_config(adapter->sessionId, qpower);
if (vos_status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
FL("failed to update qpower %d"),
vos_status);
return -EINVAL;
}
return 0;
}
/**
* hdd_sta_set_sub20_channelwidth() -
* This api function does a sub20 channel width change
* for STA while STA in disconnect state
* @adapter: HDD adapter
* @chan_width: New channel width change to
*
* Return: The VOS_STATUS code associated with performing
* the operation
*/
static VOS_STATUS
hdd_sta_set_sub20_channelwidth(hdd_adapter_t *adapter, uint32_t chan_width)
{
tHalHandle hal_ptr;
hddLog(LOGE, FL("chanwidth: %d"), chan_width);
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hddLog(LOGE, FL("hdd adapter cookie is invalid"));
return -EINVAL;
}
if (WLAN_HDD_INFRA_STATION != adapter->device_mode) {
hddLog(LOGE, FL("Only Sta Mode supported!"));
return -ENOTSUPP;
}
hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter);
if (hal_ptr == NULL) {
hddLog(LOGE, FL("hdd hal is null"));
return -EINVAL;
}
sme_set_sta_chanlist_with_sub20(hal_ptr, chan_width);
return VOS_STATUS_SUCCESS;
}
/**
* hdd_get_sub20_channelwidth() -
* This api function get sub20 channel width
* @adapter: HDD adapter
* @sub20_channelwidth: restore sub20 channel width
*
* Return: The VOS_STATUS code associated with performing
* the operation
*/
static VOS_STATUS
hdd_get_sub20_channelwidth(hdd_adapter_t *adapter, uint32_t *sub20_channelwidth)
{
tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter);
tpAniSirGlobal mac_ptr = PMAC_STRUCT(hal_ptr);
v_CONTEXT_t vos_ctx_ptr = WLAN_HDD_GET_SAP_CTX_PTR(adapter);
uint32_t sap_sub20_channelwidth;
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hddLog(LOGE, FL("hdd adapter cookie is invalid"));
return -EINVAL;
}
if (WLAN_HDD_INFRA_STATION == adapter->device_mode) {
*sub20_channelwidth = mac_ptr->sta_sub20_current_channelwidth ?
mac_ptr->sta_sub20_current_channelwidth :
mac_ptr->sub20_channelwidth;
} else if (WLAN_HDD_SOFTAP == adapter->device_mode) {
WLANSAP_get_sub20_channelwidth(vos_ctx_ptr,
&sap_sub20_channelwidth);
*sub20_channelwidth = sap_sub20_channelwidth;
} else {
hddLog(LOGE, FL("error dev mode!"));
return -EINVAL;
}
return VOS_STATUS_SUCCESS;
}
/**
* __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: EOK or other error codes.
*/
static int __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1];
int ret_val = 0;
u32 modulated_dtim;
uint16_t stats_avg_factor, tx_rate;
uint8_t set_value, retry, qpower, delay;
uint32_t abs_delay;
u32 guard_time;
u32 ftm_capab;
eHalStatus status;
struct sir_set_tx_rx_aggregation_size request;
struct sir_set_rx_reorder_timeout_val reorder_timeout;
struct sir_peer_set_rx_blocksize rx_blocksize;
VOS_STATUS vos_status;
uint32_t tx_fail_count;
uint32_t antdiv_ena, antdiv_chain;
uint32_t antdiv_selftest, antdiv_selftest_intvl;
int attr_len;
int access_policy = 0;
char vendor_ie[SIR_MAC_MAX_IE_LENGTH + 2];
bool vendor_ie_present = false, access_policy_present = false;
uint32_t ant_div_usrcfg;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret_val = wlan_hdd_validate_context(pHddCtx);
if (ret_val)
return ret_val;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX,
data, data_len,
wlan_hdd_wifi_config_policy)) {
hddLog(LOGE, FL("invalid attr"));
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]) {
ftm_capab = nla_get_u32(tb[
QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT]);
pHddCtx->cfg_ini->fine_time_meas_cap =
pHddCtx->fine_time_meas_cap_target & ftm_capab;
sme_update_fine_time_measurement_capab(pHddCtx->hHal,
pHddCtx->cfg_ini->fine_time_meas_cap);
hddLog(LOG1,
"FTM capability: user value: 0x%x, target value: 0x%x, final value: 0x%x",
ftm_capab, pHddCtx->fine_time_meas_cap_target,
pHddCtx->cfg_ini->fine_time_meas_cap);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM]) {
modulated_dtim = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM]);
status = sme_configure_modulated_dtim(pHddCtx->hHal,
pAdapter->sessionId,
modulated_dtim);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EPERM;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR]) {
stats_avg_factor = nla_get_u16(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR]);
status = sme_configure_stats_avg_factor(pHddCtx->hHal,
pAdapter->sessionId,
stats_avg_factor);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EPERM;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME]) {
guard_time = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME]);
status = sme_configure_guard_time(pHddCtx->hHal,
pAdapter->sessionId,
guard_time);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EPERM;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_RATE]) {
tx_rate = nla_get_u16(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_RATE]);
hddLog(LOG1, "Config Txrate: tx_rate received :%d", tx_rate);
status = wlan_hdd_update_tx_rate(pHddCtx, tx_rate);
if (eHAL_STATUS_SUCCESS != status)
ret_val = -EPERM;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND]) {
set_value = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND]);
hddLog(LOG1, "set_value: %d", set_value);
ret_val = hdd_enable_disable_ca_event(pHddCtx, set_value);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]) {
qpower = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]);
if (hdd_set_qpower_config(pHddCtx, pAdapter, qpower) != 0)
ret_val = -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]) {
/* if one is specified, both must be specified */
if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]) {
hddLog(LOGE,
FL("Both TX and RX MPDU Aggregation required"));
return -EINVAL;
}
request.tx_aggregation_size = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION]);
request.rx_aggregation_size = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION]);
request.vdev_id = pAdapter->sessionId;
if (request.tx_aggregation_size >=
CFG_TX_AGGREGATION_SIZE_MIN &&
request.tx_aggregation_size <=
CFG_TX_AGGREGATION_SIZE_MAX &&
request.rx_aggregation_size >=
CFG_RX_AGGREGATION_SIZE_MIN &&
request.rx_aggregation_size <=
CFG_RX_AGGREGATION_SIZE_MAX) {
vos_status = wma_set_tx_rx_aggregation_size(&request);
if (vos_status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
FL("failed to set aggr sizes err %d"),
vos_status);
ret_val = -EPERM;
}
} else {
hddLog(LOGE,
FL("TX %d RX %d MPDU aggr size not in range"),
request.tx_aggregation_size,
request.rx_aggregation_size);
ret_val = -EINVAL;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY]) {
retry = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY]);
retry = retry > CFG_NON_AGG_RETRY_MAX ?
CFG_NON_AGG_RETRY_MAX : retry;
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX]) {
status = sme_update_short_retry_limit_threshold(
pHddCtx->hHal,
pAdapter->sessionId,
retry);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_update_short_retry_limit_threshold(err=%d)"),
status);
return -EINVAL;
}
} else {
ret_val = process_wma_set_command(
(int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH,
retry, PDEV_CMD);
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY]) {
retry = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY]);
retry = retry > CFG_AGG_RETRY_MAX ?
CFG_AGG_RETRY_MAX : retry;
/* Value less than CFG_AGG_RETRY_MIN has side effect to t-put */
retry = ((retry > 0) && (retry < CFG_AGG_RETRY_MIN)) ?
CFG_AGG_RETRY_MIN : retry;
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX]) {
status = sme_update_long_retry_limit_threshold(
pHddCtx->hHal,
pAdapter->sessionId,
retry);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_update_long_retry_limit_threshold(err=%d)"),
status);
return -EINVAL;
}
} else {
ret_val = process_wma_set_command(
(int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_AGG_SW_RETRY_TH,
retry, PDEV_CMD);
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY]) {
retry = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY]);
retry = retry > CFG_MGMT_RETRY_MAX ?
CFG_MGMT_RETRY_MAX : retry;
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_MGMT_RETRY_LIMIT,
retry, PDEV_CMD);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY]) {
retry = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY]);
retry = retry > CFG_CTRL_RETRY_MAX ?
CFG_CTRL_RETRY_MAX : retry;
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_CTRL_RETRY_LIMIT,
retry, PDEV_CMD);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY]) {
delay = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY]);
delay = delay > CFG_PROPAGATION_DELAY_MAX ?
CFG_PROPAGATION_DELAY_MAX : delay;
abs_delay = delay + CFG_PROPAGATION_DELAY_BASE;
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_PROPAGATION_DELAY,
abs_delay, PDEV_CMD);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY]) {
abs_delay = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY]);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_PROPAGATION_DELAY,
abs_delay, PDEV_CMD);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT]) {
tx_fail_count = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT]);
if (tx_fail_count) {
status = sme_update_tx_fail_cnt_threshold(pHddCtx->hHal,
pAdapter->sessionId,
tx_fail_count);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_update_tx_fail_cnt_threshold (err=%d)"),
status);
return -EINVAL;
}
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]) {
vos_mem_zero(&vendor_ie[0], SIR_MAC_MAX_IE_LENGTH + 2);
attr_len = nla_len(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST]);
if (attr_len < 0 || attr_len > SIR_MAC_MAX_IE_LENGTH + 2) {
hddLog(LOGE, FL("Invalid value. attr_len %d"),
attr_len);
return -EINVAL;
}
nla_memcpy(&vendor_ie,
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST],
attr_len);
vendor_ie_present = true;
hddLog(LOG1, FL("Access policy vendor ie present.attr_len %d"),
attr_len);
vos_trace_hex_dump(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
&vendor_ie[0], attr_len);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]) {
access_policy = (int) nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY]);
if ((access_policy < QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED) ||
(access_policy > QCA_ACCESS_POLICY_DENY_UNLESS_LISTED)){
hddLog(LOGE, FL("Invalid value. access_policy %d"),
access_policy);
return -EINVAL;
}
access_policy_present = true;
hddLog(LOG1, FL("Access policy present. access_policy %d"),
access_policy);
}
if (vendor_ie_present && access_policy_present) {
if (access_policy == QCA_ACCESS_POLICY_DENY_UNLESS_LISTED) {
access_policy =
WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED;
}
else {
access_policy = WLAN_HDD_VENDOR_IE_ACCESS_NONE;
}
hddLog(LOG1, FL("calling sme_update_access_policy_vendor_ie"));
status = sme_update_access_policy_vendor_ie(pHddCtx->hHal,
pAdapter->sessionId, &vendor_ie[0],
access_policy);
if (status == eHAL_STATUS_FAILURE) {
hddLog(LOGE, FL(
"Failed to set vendor ie and access policy."));
return -EINVAL;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]) {
antdiv_ena = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA]);
hddLog(LOG1, FL("antdiv_ena: %d"), antdiv_ena);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ENA_ANT_DIV,
antdiv_ena, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set antdiv_ena"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]) {
antdiv_chain = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN]);
hddLog(LOG1, FL("antdiv_chain: %d"), antdiv_chain);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_FORCE_CHAIN_ANT,
antdiv_chain, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set antdiv_chain"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]) {
antdiv_selftest = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST]);
hddLog(LOG1, FL("antdiv_selftest: %d"), antdiv_selftest);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST,
antdiv_selftest, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set antdiv_selftest"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]) {
antdiv_selftest_intvl = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL]);
hddLog(LOG1, FL("antdiv_selftest_intvl: %d"),
antdiv_selftest_intvl);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL,
antdiv_selftest_intvl, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set antdiv_selftest_intvl"));
return ret_val;
}
}
#define RX_TIMEOUT_VAL_MIN 10
#define RX_TIMEOUT_VAL_MAX 1000
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND]) {
/* if one is specified, all must be specified */
if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND]) {
hddLog(LOGE,
FL("four AC timeout val are required MAC"));
return -EINVAL;
}
reorder_timeout.rx_timeout_pri[0] = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE]);
reorder_timeout.rx_timeout_pri[1] = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO]);
reorder_timeout.rx_timeout_pri[2] = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT]);
reorder_timeout.rx_timeout_pri[3] = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND]);
/* timeout value is required to be in the rang 10 to 1000ms */
if (reorder_timeout.rx_timeout_pri[0] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[0] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[1] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[1] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[2] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[2] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[3] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[3] <= RX_TIMEOUT_VAL_MAX) {
vos_status = sme_set_reorder_timeout(pHddCtx->hHal,
&reorder_timeout);
if (vos_status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
FL("failed to set reorder timeout err %d"),
vos_status);
ret_val = -EPERM;
}
} else {
hddLog(LOGE,
FL("one of the timeout value is not in range"));
ret_val = -EINVAL;
}
}
#define WINDOW_SIZE_VAL_MIN 1
#define WINDOW_SIZE_VAL_MAX 64
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT]) {
/* if one is specified, both must be specified */
if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT]) {
hddLog(LOGE,
FL("Both Peer MAC and windows limit required"));
return -EINVAL;
}
memcpy(&rx_blocksize.peer_macaddr,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC]),
sizeof(rx_blocksize.peer_macaddr)),
rx_blocksize.vdev_id = pAdapter->sessionId;
set_value = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT]);
/* maximum window size is 64 */
if (set_value >= WINDOW_SIZE_VAL_MIN &&
set_value <= WINDOW_SIZE_VAL_MAX) {
rx_blocksize.rx_block_ack_win_limit = set_value;
vos_status = sme_set_rx_set_blocksize(pHddCtx->hHal,
&rx_blocksize);
if (vos_status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
FL("failed to set aggr sizes err %d"),
vos_status);
ret_val = -EPERM;
}
} else {
hddLog(LOGE, FL("window size val is not in range"));
ret_val = -EINVAL;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) {
bool manual_set_sub20;
uint32_t sub20_chan_width;
sub20_chan_width = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]);
hddLog(LOGE, FL("SUB20 %d"), sub20_chan_width);
switch (sub20_chan_width) {
case NL80211_CHAN_WIDTH_5:
sub20_chan_width = SUB20_MODE_5MHZ;
break;
case NL80211_CHAN_WIDTH_10:
sub20_chan_width = SUB20_MODE_10MHZ;
break;
case NL80211_CHAN_WIDTH_20_NOHT:
sub20_chan_width = SUB20_MODE_NONE;
break;
default:
hddLog(LOGE, FL("invalid param %d"), sub20_chan_width);
return -EINVAL;
}
manual_set_sub20 =
hdd_sub20_channelwidth_can_set(pAdapter,
sub20_chan_width);
if (!manual_set_sub20) {
hddLog(LOGE, FL("STA can't set sub20 chanwidth"));
return -EINVAL;
}
hdd_sta_set_sub20_channelwidth(pAdapter, sub20_chan_width);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]) {
if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]) {
hddLog(LOGE, FL("Both probe and stay period required"));
return -EINVAL;
}
ant_div_usrcfg = ANT_DIV_SET_PERIOD(
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD]),
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD]));
hddLog(LOG1, FL("ant div set period: %x"), ant_div_usrcfg);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set ant div period"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF]) {
ant_div_usrcfg = ANT_DIV_SET_SNR_DIFF(
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF]));
hddLog(LOG1, FL("ant div set snr diff: %x"), ant_div_usrcfg);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set ant snr diff"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME]) {
ant_div_usrcfg = ANT_DIV_SET_PROBE_DWELL_TIME(
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME]));
hddLog(LOG1, FL("ant div set probe dewll time: %x"), ant_div_usrcfg);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set ant div probe dewll time"));
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] ||
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]) {
if (!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] ||
!tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]) {
hddLog(LOGE, FL("Mgmt snr, data snr and ack snr weight are required"));
return -EINVAL;
}
ant_div_usrcfg = ANT_DIV_SET_WEIGHT(
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT]),
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT]),
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT]));
hddLog(LOG1, FL("ant div set weight: %x"), ant_div_usrcfg);
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (ret_val) {
hddLog(LOG1, FL("Failed to set ant div weight"));
return ret_val;
}
}
return ret_val;
}
/**
* wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_cfg80211_wifi_configuration_get() -
* Get the wifi configuration info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1];
uint32_t sub20_chan_width = 0;
int status;
struct sk_buff *reply_skb;
uint32_t skb_len = 0, count = 0;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data,
data_len, wlan_hdd_wifi_config_policy)) {
hddLog(LOGE, FL("WIFI_CFG_GET NL CMD parsing failed"));
return -EINVAL;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) {
hdd_get_sub20_channelwidth(adapter, &sub20_chan_width);
switch (sub20_chan_width) {
case SUB20_MODE_5MHZ:
sub20_chan_width = NL80211_CHAN_WIDTH_5;
break;
case SUB20_MODE_10MHZ:
sub20_chan_width = NL80211_CHAN_WIDTH_10;
break;
case SUB20_MODE_NONE:
sub20_chan_width = NL80211_CHAN_WIDTH_20_NOHT;
break;
default:
hddLog(LOGE, FL("invalid val %d"), sub20_chan_width);
return -EINVAL;
}
hddLog(LOGE, FL("SUB20 chanwidth %d"), sub20_chan_width);
skb_len += sizeof(sub20_chan_width);
count++;
}
if (count == 0) {
hddLog(LOGE, FL("unknown attribute in get_wifi_cfg request"));
return -EINVAL;
}
skb_len += NLMSG_HDRLEN;
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
if (!reply_skb)
goto error_skb_fail;
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH]) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH,
sub20_chan_width))
goto error_nla_fail;
}
return cfg80211_vendor_cmd_reply(reply_skb);
error_skb_fail:
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
error_nla_fail:
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_wifi_configuration_get() - Get the wifi configuration info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_wifi_configuration_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_configuration_get(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_WLAN_TDLS
/* TDLS capabilities params */
#define PARAM_MAX_TDLS_SESSION \
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS
#define PARAM_TDLS_FEATURE_SUPPORT \
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED
/**
* __wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilites.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function provides TDLS capabilities
*
* Return: 0 on success and errno on failure
*/
static int
__wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct sk_buff *skb;
uint32_t set = 0;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, (2 * sizeof(u32)) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
goto fail;
}
if (FALSE == hdd_ctx->cfg_ini->fEnableTDLSSupport) {
hddLog(LOGE,
FL("TDLS feature not Enabled or Not supported in FW"));
if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, 0) ||
nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, 0)) {
hddLog(LOGE, FL("nla put fail"));
goto fail;
}
} else {
set = set | WIFI_TDLS_SUPPORT;
set = set | (hdd_ctx->cfg_ini->fTDLSExternalControl ?
WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT : 0);
set = set | (hdd_ctx->cfg_ini->fEnableTDLSOffChannel ?
WIIF_TDLS_OFFCHANNEL_SUPPORT : 0);
hddLog(LOG1, FL("TDLS Feature supported value %x"), set);
if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION,
hdd_ctx->max_num_tdls_sta) ||
nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT,
set)) {
hddLog(LOGE, FL("nla put fail"));
goto fail;
}
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilites.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function provides TDLS capabilities
*
* Return: 0 on success and errno on failure
*/
static int
wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_tdls_capabilities(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
static const struct
nla_policy
qca_wlan_vendor_wifi_logger_start_policy
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]
= {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]
= {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]
= {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_wifi_logger_start() - This function is used to enable
* or disable the collection of packet statistics from the firmware
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to enable or disable the collection of packet
* statistics from the firmware
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
eHalStatus status;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1];
struct sir_wifi_start_log start_log;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_start_policy)) {
hddLog(LOGE, FL("Invalid attribute"));
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) {
hddLog(LOGE, FL("attr ATTR failed"));
return -EINVAL;
}
start_log.ring_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]);
hddLog(LOG1, FL("Ring ID=%d"), start_log.ring_id);
/* Parse and fetch verbose level */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) {
hddLog(LOGE, FL("attr verbose_level failed"));
return -EINVAL;
}
start_log.verbose_level = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]);
hddLog(LOG1, FL("verbose_level=%d"), start_log.verbose_level);
/* Parse and fetch flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) {
hddLog(LOGE, FL("attr flag failed"));
return -EINVAL;
}
start_log.flag = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]);
hddLog(LOG1, FL("flag=%d"), start_log.flag);
vos_set_ring_log_level(start_log.ring_id, start_log.verbose_level);
if (start_log.ring_id == RING_ID_WAKELOCK) {
/* Start/stop wakelock events */
if (start_log.verbose_level > WLAN_LOG_LEVEL_OFF)
vos_set_wakelock_logging(true);
else
vos_set_wakelock_logging(false);
return 0;
}
status = sme_wifi_start_logger(hdd_ctx->hHal, start_log);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_wifi_start_logger failed(err=%d)"),
status);
return -EINVAL;
}
return 0;
}
static const struct
nla_policy
qca_wlan_vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {
.type = NLA_BINARY,
.len = HDD_MAC_ADDR_LEN},
};
/**
* __wlan_hdd_cfg80211_get_link_properties() - This function is used to
* get link properties like nss, rate flags and operating frequency for
* the connection with the given peer.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function return the above link properties on success.
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *hdd_sta_ctx;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1];
uint8_t peer_mac[VOS_MAC_ADDR_SIZE];
uint32_t sta_id;
struct sk_buff *reply_skb;
uint32_t rate_flags = 0;
uint8_t nss;
uint8_t final_rate_flags = 0;
uint32_t freq;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (0 != wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
qca_wlan_vendor_attr_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid attribute"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Attribute peerMac not provided for mode=%d"),
adapter->device_mode);
return -EINVAL;
}
if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < sizeof(peer_mac)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Attribute peerMac is invalid=%d"),
adapter->device_mode);
return -EINVAL;
}
memcpy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
sizeof(peer_mac));
hddLog(VOS_TRACE_LEVEL_INFO,
FL("peerMac="MAC_ADDRESS_STR" for device_mode:%d"),
MAC_ADDR_ARRAY(peer_mac), adapter->device_mode);
if (adapter->device_mode == WLAN_HDD_INFRA_STATION ||
adapter->device_mode == WLAN_HDD_P2P_CLIENT) {
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if ((hdd_sta_ctx->conn_info.connState !=
eConnectionState_Associated) ||
!vos_mem_compare(hdd_sta_ctx->conn_info.bssId, peer_mac,
VOS_MAC_ADDRESS_LEN)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not Associated to mac "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = hdd_sta_ctx->conn_info.nss;
freq = vos_chan_to_freq(
hdd_sta_ctx->conn_info.operationChannel);
rate_flags = hdd_sta_ctx->conn_info.rate_flags;
} else if (adapter->device_mode == WLAN_HDD_P2P_GO ||
adapter->device_mode == WLAN_HDD_SOFTAP) {
for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) {
if (adapter->aStaInfo[sta_id].isUsed &&
!vos_is_macaddr_broadcast(
&adapter->aStaInfo[sta_id].macAddrSTA) &&
vos_mem_compare(
&adapter->aStaInfo[sta_id].macAddrSTA,
peer_mac, VOS_MAC_ADDRESS_LEN))
break;
}
if (WLAN_MAX_STA_COUNT == sta_id) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("No active peer with mac="MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = adapter->aStaInfo[sta_id].nss;
freq = vos_chan_to_freq(
(WLAN_HDD_GET_AP_CTX_PTR(adapter))->operatingChannel);
rate_flags = adapter->aStaInfo[sta_id].rate_flags;
} else {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not Associated! with mac"MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
if (rate_flags & eHAL_TX_RATE_VHT80) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
} else if (rate_flags & eHAL_TX_RATE_VHT40) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
} else if (rate_flags & eHAL_TX_RATE_VHT20) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
} else if (rate_flags & (eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40)) {
final_rate_flags |= RATE_INFO_FLAGS_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
if (rate_flags & eHAL_TX_RATE_HT40)
final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
if (rate_flags & eHAL_TX_RATE_SGI) {
if (!(final_rate_flags & RATE_INFO_FLAGS_VHT_MCS))
final_rate_flags |= RATE_INFO_FLAGS_MCS;
final_rate_flags |= RATE_INFO_FLAGS_SHORT_GI;
}
}
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(u8) + sizeof(u8) + sizeof(u32) + NLMSG_HDRLEN);
if (NULL == reply_skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("getLinkProperties: skb alloc failed"));
return -EINVAL;
}
if (nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_NSS,
nss) ||
nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_RATE_FLAGS,
final_rate_flags) ||
nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_FREQ,
freq)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_put failed"));
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* wlan_hdd_cfg80211_get_link_properties() - This function is used to
* get link properties like nss, rate flags and operating frequency for
* the connection with the given peer.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function return the above link properties on success.
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_link_properties(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_cfg80211_wifi_logger_start() - Wrapper function used to enable
* or disable the collection of packet statistics from the firmware
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to enable or disable the collection of packet
* statistics from the firmware
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_logger_start(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct
nla_policy
qca_wlan_vendor_wifi_logger_get_ring_data_policy
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]
= {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Flush per packet stats
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to flush or retrieve the per packet statistics from
* the driver
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
eHalStatus status;
VOS_STATUS ret;
uint32_t ring_id;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1];
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_get_ring_data_policy)) {
hddLog(LOGE, FL("Invalid attribute"));
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) {
hddLog(LOGE, FL("attr ATTR failed"));
return -EINVAL;
}
ring_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]);
if (ring_id == RING_ID_PER_PACKET_STATS) {
wlan_logging_set_per_pkt_stats();
hddLog(LOG1, FL("Flushing/Retrieving packet stats"));
} else if (ring_id == RING_ID_DRIVER_DEBUG) {
/*
* As part of DRIVER ring ID, flush both driver and fw logs.
* For other Ring ID's driver doesn't have any rings to flush
*/
hddLog(LOG1, FL("Bug report triggered by framework"));
ret = vos_flush_logs(WLAN_LOG_TYPE_NON_FATAL,
WLAN_LOG_INDICATOR_FRAMEWORK,
WLAN_LOG_REASON_CODE_UNUSED,
DUMP_VOS_TRACE | DUMP_PACKET_TRACE);
if (VOS_STATUS_SUCCESS != ret) {
hddLog(LOGE, FL("Failed to trigger bug report"));
return -EINVAL;
}
} else
wlan_report_log_completion(FALSE, WLAN_LOG_INDICATOR_FRAMEWORK,
WLAN_LOG_REASON_CODE_UNUSED);
return 0;
}
/**
* wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Wrapper to flush packet stats
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to flush or retrieve the per packet statistics from
* the driver
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
/**
* hdd_map_req_id_to_pattern_id() - map request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* if the slot is available, store the request id and return pattern id
* if entry exists, return the pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_map_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) {
if (hdd_ctx->op_ctx.op_table[i].request_id == MAX_REQUEST_ID) {
hdd_ctx->op_ctx.op_table[i].request_id = request_id;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
} else if (hdd_ctx->op_ctx.op_table[i].request_id ==
request_id) {
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -EINVAL;
}
/**
* hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* reset request id to 0 (slot available again) and
* return pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_unmap_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) {
if (hdd_ctx->op_ctx.op_table[i].request_id == request_id) {
hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -EINVAL;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID
#define PARAM_CONTROL \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL
#define PARAM_IP_PACKET \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA
#define PARAM_SRC_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR
#define PARAM_DST_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR
#define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD
/**
* wlan_hdd_add_tx_ptrn() - add tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a AddTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_add_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
struct nlattr **tb)
{
struct sSirAddPeriodicTxPtrn *add_req;
eHalStatus status;
uint32_t request_id, ret, len;
uint8_t pattern_id = 0;
v_MACADDR_t dst_addr;
uint16_t eth_type = htons(ETH_P_IP);
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
add_req = vos_mem_malloc(sizeof(*add_req));
if (!add_req) {
hddLog(LOGE, FL("memory allocation failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
if (request_id == MAX_REQUEST_ID) {
hddLog(LOGE, FL("request_id cannot be MAX"));
goto fail;
}
hddLog(LOG1, FL("Request Id: %u"), request_id);
if (!tb[PARAM_PERIOD]) {
hddLog(LOGE, FL("attr period failed"));
goto fail;
}
add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]);
hddLog(LOG1, FL("Period: %u ms"), add_req->usPtrnIntervalMs);
if (add_req->usPtrnIntervalMs == 0) {
hddLog(LOGE, FL("Invalid interval zero, return failure"));
goto fail;
}
if (!tb[PARAM_SRC_MAC_ADDR]) {
hddLog(LOGE, FL("attr source mac address failed"));
goto fail;
}
nla_memcpy(add_req->macAddress, tb[PARAM_SRC_MAC_ADDR],
VOS_MAC_ADDR_SIZE);
hddLog(LOG1, "input src mac address: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(add_req->macAddress));
if (memcmp(add_req->macAddress, adapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE)) {
hddLog(LOGE, FL("input src mac address and connected ap bssid are different"));
goto fail;
}
if (!tb[PARAM_DST_MAC_ADDR]) {
hddLog(LOGE, FL("attr dst mac address failed"));
goto fail;
}
nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], VOS_MAC_ADDR_SIZE);
hddLog(LOG1, "input dst mac address: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(dst_addr.bytes));
if (!tb[PARAM_IP_PACKET]) {
hddLog(LOGE, FL("attr ip packet failed"));
goto fail;
}
add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]);
hddLog(LOG1, FL("IP packet len: %u"), add_req->ucPtrnSize);
if (add_req->ucPtrnSize < 0 ||
add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE -
HDD_ETH_HEADER_LEN)) {
hddLog(LOGE, FL("Invalid IP packet len: %d"),
add_req->ucPtrnSize);
goto fail;
}
len = 0;
vos_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, VOS_MAC_ADDR_SIZE);
len += VOS_MAC_ADDR_SIZE;
vos_mem_copy(&add_req->ucPattern[len], add_req->macAddress,
VOS_MAC_ADDR_SIZE);
len += VOS_MAC_ADDR_SIZE;
vos_mem_copy(&add_req->ucPattern[len], &eth_type, 2);
len += 2;
/*
* This is the IP packet, add 14 bytes Ethernet (802.3) header
* ------------------------------------------------------------
* | 14 bytes Ethernet (802.3) header | IP header and payload |
* ------------------------------------------------------------
*/
vos_mem_copy(&add_req->ucPattern[len],
nla_data(tb[PARAM_IP_PACKET]),
add_req->ucPtrnSize);
add_req->ucPtrnSize += len;
ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret) {
hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
goto fail;
}
add_req->ucPtrnId = pattern_id;
hddLog(LOG1, FL("pattern id: %d"), add_req->ucPtrnId);
status = sme_AddPeriodicTxPtrn(hdd_ctx->hHal, add_req);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_AddPeriodicTxPtrn failed (err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(add_req);
return 0;
fail:
vos_mem_free(add_req);
return -EINVAL;
}
/**
* wlan_hdd_del_tx_ptrn() - delete tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a DelTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_del_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
struct nlattr **tb)
{
struct sSirDelPeriodicTxPtrn *del_req;
eHalStatus status;
uint32_t request_id, ret;
uint8_t pattern_id = 0;
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
return -EINVAL;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
if (request_id == MAX_REQUEST_ID) {
hddLog(LOGE, FL("request_id cannot be MAX"));
return -EINVAL;
}
ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret) {
hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
return -EINVAL;
}
del_req = vos_mem_malloc(sizeof(*del_req));
if (!del_req) {
hddLog(LOGE, FL("memory allocation failed"));
return -ENOMEM;
}
vos_mem_copy(del_req->macAddress, adapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE);
hddLog(LOG1, MAC_ADDRESS_STR, MAC_ADDR_ARRAY(del_req->macAddress));
del_req->ucPtrnId = pattern_id;
hddLog(LOG1, FL("Request Id: %u Pattern id: %d"),
request_id, del_req->ucPtrnId);
status = sme_DelPeriodicTxPtrn(hdd_ctx->hHal, del_req);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_DelPeriodicTxPtrn failed (err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(del_req);
return 0;
fail:
vos_mem_free(del_req);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
uint8_t control;
int ret;
static const struct nla_policy policy[PARAM_MAX + 1] = {
[PARAM_REQUEST_ID] = { .type = NLA_U32 },
[PARAM_CONTROL] = { .type = NLA_U32 },
[PARAM_SRC_MAC_ADDR] = { .type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE },
[PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE },
[PARAM_PERIOD] = { .type = NLA_U32 },
};
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (!sme_IsFeatureSupportedByFW(WLAN_PERIODIC_TX_PTRN)) {
hddLog(LOGE,
FL("Periodic Tx Pattern Offload feature is not supported in FW!"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len, policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[PARAM_CONTROL]) {
hddLog(LOGE, FL("attr control failed"));
return -EINVAL;
}
control = nla_get_u32(tb[PARAM_CONTROL]);
hddLog(LOG1, FL("Control: %d"), control);
if (control == WLAN_START_OFFLOADED_PACKETS)
return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb);
else if (control == WLAN_STOP_OFFLOADED_PACKETS)
return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb);
else {
hddLog(LOGE, FL("Invalid control: %d"), control);
return -EINVAL;
}
}
/*
* done with short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_CONTROL
#undef PARAM_IP_PACKET
#undef PARAM_SRC_MAC_ADDR
#undef PARAM_DST_MAC_ADDR
#undef PARAM_PERIOD
/**
* wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_offloaded_packets(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_monitor_rssi()
*/
#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX
#define PARAM_REQUEST_ID QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID
#define PARAM_CONTROL QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL
#define PARAM_MIN_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI
#define PARAM_MAX_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI
/**
* __wlan_hdd_cfg80211_monitor_rssi() - monitor rssi
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
struct rssi_monitor_req req;
eHalStatus status;
int ret;
uint32_t control;
static const struct nla_policy policy[PARAM_MAX + 1] = {
[PARAM_REQUEST_ID] = { .type = NLA_U32 },
[PARAM_CONTROL] = { .type = NLA_U32 },
[PARAM_MIN_RSSI] = { .type = NLA_S8 },
[PARAM_MAX_RSSI] = { .type = NLA_S8 },
};
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len, policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
return -EINVAL;
}
if (!tb[PARAM_CONTROL]) {
hddLog(LOGE, FL("attr control failed"));
return -EINVAL;
}
req.request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
req.session_id = adapter->sessionId;
control = nla_get_u32(tb[PARAM_CONTROL]);
if (control == QCA_WLAN_RSSI_MONITORING_START) {
req.control = true;
if (!tb[PARAM_MIN_RSSI]) {
hddLog(LOGE, FL("attr min rssi failed"));
return -EINVAL;
}
if (!tb[PARAM_MAX_RSSI]) {
hddLog(LOGE, FL("attr max rssi failed"));
return -EINVAL;
}
req.min_rssi = nla_get_s8(tb[PARAM_MIN_RSSI]);
req.max_rssi = nla_get_s8(tb[PARAM_MAX_RSSI]);
if (!(req.min_rssi < req.max_rssi)) {
hddLog(LOGW, FL("min_rssi: %d must be less than max_rssi: %d"),
req.min_rssi, req.max_rssi);
return -EINVAL;
}
hddLog(LOG1, FL("Min_rssi: %d Max_rssi: %d"),
req.min_rssi, req.max_rssi);
} else if (control == QCA_WLAN_RSSI_MONITORING_STOP)
req.control = false;
else {
hddLog(LOGE, FL("Invalid control cmd: %d"), control);
return -EINVAL;
}
hddLog(LOG1, FL("Request Id: %u Session_id: %d Control: %d"),
req.request_id, req.session_id, req.control);
status = sme_set_rssi_monitoring(hdd_ctx->hHal, &req);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_set_rssi_monitoring failed(err=%d)"), status);
return -EINVAL;
}
return 0;
}
/*
* done with short names for the global vendor params
* used by __wlan_hdd_cfg80211_monitor_rssi()
*/
#undef PARAM_MAX
#undef PARAM_CONTROL
#undef PARAM_REQUEST_ID
#undef PARAM_MAX_RSSI
#undef PARAM_MIN_RSSI
/**
* wlan_hdd_cfg80211_monitor_rssi() - SSR wrapper to rssi monitoring
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int
wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_monitor_rssi(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_rssi_threshold_breached() - rssi breached NL event
* @hddctx: HDD context
* @data: rssi breached event data
*
* This function reads the rssi breached event %data and fill in the skb with
* NL attributes and send up the NL event.
* This callback execute in atomic context and must not invoke any
* blocking calls.
*
* Return: none
*/
void hdd_rssi_threshold_breached(void *hddctx,
struct rssi_breach_event *data)
{
hdd_context_t *hdd_ctx = hddctx;
struct sk_buff *skb;
int flags = vos_get_gfp_flags();
ENTER();
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (!data) {
hddLog(LOGE, FL("data is null"));
return;
}
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL,
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX,
flags);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(LOG1, "Req Id: %u Current rssi: %d",
data->request_id, data->curr_rssi);
hddLog(LOG1, "Current BSSID: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(data->curr_bssid.bytes));
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
data->request_id) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
sizeof(data->curr_bssid), data->curr_bssid.bytes) ||
nla_put_s8(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
data->curr_rssi)) {
hddLog(LOGE, FL("nla put fail"));
goto fail;
}
cfg80211_vendor_event(skb, flags);
return;
fail:
kfree_skb(skb);
return;
}
#define PWR_SAVE_FAIL_CMD_INDEX \
QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX
/**
* hdd_chip_pwr_save_fail_detected() - chip power save failure detected
* NL event
* @hddctx: HDD context
* @data: chip power save failure detected data
*
* This function reads the chip power save failure detected data and fill in
* the skb with NL attributes and send up the NL event.
* This callback execute in atomic context and must not invoke any
* blocking calls.
*
* Return: none
*/
void hdd_chip_pwr_save_fail_detected_cb(void *hddctx,
struct chip_pwr_save_fail_detected_params
*data)
{
hdd_context_t *hdd_ctx = hddctx;
struct sk_buff *skb;
int flags = vos_get_gfp_flags();
ENTER();
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (!data) {
hddLog(LOGE, FL("data is null"));
return;
}
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL, NLMSG_HDRLEN +
sizeof(data-> failure_reason_code) +
NLMSG_HDRLEN, PWR_SAVE_FAIL_CMD_INDEX,
flags);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(LOG1, "failure reason code: %u, wake lock bitmap : %u %u %u %u",
data->failure_reason_code,
data->wake_lock_bitmap[0],
data->wake_lock_bitmap[1],
data->wake_lock_bitmap[2],
data->wake_lock_bitmap[3]);
if (nla_put_u32(skb,
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON,
data->failure_reason_code))
goto fail;
cfg80211_vendor_event(skb, flags);
EXIT();
return;
fail:
kfree_skb(skb);
EXIT();
return;
}
#undef PWR_SAVE_FAIL_CMD_INDEX
static const struct nla_policy
ns_offload_set_policy[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG] = {.type = NLA_U8},
};
/**
* __wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int status;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX + 1];
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return status;
if (TRUE != pHddCtx->cfg_ini->fhostNSOffload) {
hddLog(LOGE, FL("ND Offload not supported"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX,
(struct nlattr *)data,
data_len, ns_offload_set_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]) {
hddLog(LOGE, FL("ND Offload flag attribute not present"));
return -EINVAL;
}
pHddCtx->ns_offload_enable =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]);
/*
* If active mode offload is enabled configure the nsoffload
* enable/disable request from the upper layer.
*/
if (pHddCtx->cfg_ini->active_mode_offload) {
hddLog(LOG1,
FL("Configure NS offload with command: %d"),
pHddCtx->ns_offload_enable);
hdd_conf_ns_offload(pAdapter, pHddCtx->ns_offload_enable);
}
return 0;
}
/**
* wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_ns_offload(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_cfg80211_setband() - set band
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
int ret;
static const struct nla_policy policy[QCA_WLAN_VENDOR_ATTR_MAX + 1]
= {[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = { .type = NLA_U32 } };
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) {
hddLog(LOGE, FL("attr SETBAND_VALUE failed"));
return -EINVAL;
}
return hdd_setBand(dev,
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]));
}
/**
* wlan_hdd_cfg80211_setband() - Wrapper to setband
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_setband(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_bpf_offload()
*/
#define BPF_INVALID \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID
#define BPF_SET_RESET \
QCA_WLAN_VENDOR_ATTR_SET_RESET_PACKET_FILTER
#define BPF_VERSION \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION
#define BPF_FILTER_ID \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID
#define BPF_PACKET_SIZE \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE
#define BPF_CURRENT_OFFSET \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET
#define BPF_PROGRAM \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM
#define BPF_MAX \
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX
static const struct nla_policy
wlan_hdd_bpf_offload_policy[BPF_MAX + 1] = {
[BPF_SET_RESET] = {.type = NLA_U32},
[BPF_VERSION] = {.type = NLA_U32},
[BPF_FILTER_ID] = {.type = NLA_U32},
[BPF_PACKET_SIZE] = {.type = NLA_U32},
[BPF_CURRENT_OFFSET] = {.type = NLA_U32},
[BPF_PROGRAM] = {.type = NLA_U8},
};
/**
* hdd_get_bpf_offload_cb() - Callback function to BPF Offload
* @hdd_context: hdd_context
* @bpf_get_offload: struct for get offload
*
* This function receives the response/data from the lower layer and
* checks to see if the thread is still waiting then post the results to
* upper layer, if the request has timed out then ignore.
*
* Return: None
*/
void hdd_get_bpf_offload_cb(void *hdd_context,
struct sir_bpf_get_offload *data)
{
hdd_context_t *hdd_ctx = hdd_context;
struct hdd_bpf_context *context;
ENTER();
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (!data) {
hddLog(LOGE, FL("Data is null"));
return;
}
spin_lock(&hdd_context_lock);
context = &bpf_context;
/* The caller presumably timed out so there is nothing we can do */
if (context->magic != BPF_CONTEXT_MAGIC) {
spin_unlock(&hdd_context_lock);
return;
}
/* context is valid so caller is still waiting */
/* paranoia: invalidate the magic */
context->magic = 0;
context->capability_response = *data;
complete(&context->completion);
spin_unlock(&hdd_context_lock);
return;
}
/**
* hdd_post_get_bpf_capabilities_rsp() - Callback function to BPF Offload
* @hdd_context: hdd_context
* @bpf_get_offload: struct for get offload
*
* Return: 0 on success, error number otherwise.
*/
static int hdd_post_get_bpf_capabilities_rsp(hdd_context_t *hdd_ctx,
struct sir_bpf_get_offload *bpf_get_offload)
{
struct sk_buff *skb;
uint32_t nl_buf_len;
ENTER();
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len +=
(sizeof(bpf_get_offload->max_bytes_for_bpf_inst) + NLA_HDRLEN) +
(sizeof(bpf_get_offload->bpf_version) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, "BPF Version: %u BPF max bytes: %u",
bpf_get_offload->bpf_version,
bpf_get_offload->max_bytes_for_bpf_inst);
if (nla_put_u32(skb, BPF_PACKET_SIZE,
bpf_get_offload->max_bytes_for_bpf_inst) ||
nla_put_u32(skb, BPF_VERSION, bpf_get_offload->bpf_version)) {
hddLog(LOGE, FL("nla put failure"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
EXIT();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* hdd_get_bpf_offload - Get BPF offload Capabilities
* @hdd_ctx: Hdd context
*
* Return: 0 on success, errno on failure
*/
static int hdd_get_bpf_offload(hdd_context_t *hdd_ctx)
{
unsigned long rc;
struct hdd_bpf_context *context;
eHalStatus hstatus;
int ret;
ENTER();
spin_lock(&hdd_context_lock);
context = &bpf_context;
context->magic = BPF_CONTEXT_MAGIC;
INIT_COMPLETION(context->completion);
spin_unlock(&hdd_context_lock);
hstatus = sme_get_bpf_offload_capabilities(hdd_ctx->hHal);
if (!HAL_STATUS_SUCCESS(hstatus)) {
hddLog(LOGE, FL("Unable to retrieve BPF caps"));
return -EINVAL;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->completion,
msecs_to_jiffies(WLAN_WAIT_TIME_BPF));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
spin_lock(&hdd_context_lock);
context->magic = 0;
spin_unlock(&hdd_context_lock);
return -ETIMEDOUT;
}
ret = hdd_post_get_bpf_capabilities_rsp(hdd_ctx,
&bpf_context.capability_response);
if (ret)
hddLog(LOGE, FL("Failed to post get bpf capabilities"));
EXIT();
return ret;
}
/**
* hdd_set_reset_bpf_offload - Post set/reset bpf to SME
* @hdd_ctx: Hdd context
* @tb: Length of @data
* @adapter: pointer to adapter struct
*
* Return: 0 on success; errno on failure
*/
static int hdd_set_reset_bpf_offload(hdd_context_t *hdd_ctx,
struct nlattr **tb,
hdd_adapter_t *adapter)
{
struct sir_bpf_set_offload *bpf_set_offload;
eHalStatus hstatus;
int prog_len;
int ret_val = -EINVAL;
ENTER();
if (adapter->device_mode == WLAN_HDD_INFRA_STATION ||
adapter->device_mode == WLAN_HDD_P2P_CLIENT) {
if (!hdd_connIsConnected(
WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
}
bpf_set_offload = vos_mem_malloc(sizeof(*bpf_set_offload));
if (bpf_set_offload == NULL) {
hddLog(LOGE, FL("vos_mem_malloc failed for bpf_set_offload"));
return -ENOMEM;
}
vos_mem_zero(bpf_set_offload, sizeof(*bpf_set_offload));
/* Parse and fetch bpf packet size */
if (!tb[BPF_PACKET_SIZE]) {
hddLog(LOGE, FL("attr bpf packet size failed"));
goto fail;
}
bpf_set_offload->total_length = nla_get_u32(tb[BPF_PACKET_SIZE]);
if (!bpf_set_offload->total_length) {
hddLog(LOG1, FL("BPF reset packet filter received"));
goto post_sme;
}
/* Parse and fetch bpf program */
if (!tb[BPF_PROGRAM]) {
hddLog(LOGE, FL("attr bpf program failed"));
goto fail;
}
prog_len = nla_len(tb[BPF_PROGRAM]);
bpf_set_offload->program = vos_mem_malloc(sizeof(uint8_t) * prog_len);
if (!bpf_set_offload->program) {
hddLog(LOGE, FL("failed to allocate memory for bpf filter"));
ret_val = -ENOMEM;
goto fail;
}
bpf_set_offload->current_length = prog_len;
nla_memcpy(bpf_set_offload->program, tb[BPF_PROGRAM], prog_len);
bpf_set_offload->session_id = adapter->sessionId;
hddLog(LOG1, FL("BPF set instructions"));
VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
bpf_set_offload->program, prog_len);
/* Parse and fetch filter Id */
if (!tb[BPF_FILTER_ID]) {
hddLog(LOGE, FL("attr filter id failed"));
goto fail;
}
bpf_set_offload->filter_id = nla_get_u32(tb[BPF_FILTER_ID]);
/* Parse and fetch current offset */
if (!tb[BPF_CURRENT_OFFSET]) {
hddLog(LOGE, FL("attr current offset failed"));
goto fail;
}
bpf_set_offload->current_offset = nla_get_u32(tb[BPF_CURRENT_OFFSET]);
post_sme:
hddLog(LOG1, FL("Posting BPF SET/RESET to SME, session_id: %d Bpf Version: %d filter ID: %d total_length: %d current_length: %d current offset: %d"),
bpf_set_offload->session_id,
bpf_set_offload->version,
bpf_set_offload->filter_id,
bpf_set_offload->total_length,
bpf_set_offload->current_length,
bpf_set_offload->current_offset);
hstatus = sme_set_bpf_instructions(hdd_ctx->hHal, bpf_set_offload);
if (!HAL_STATUS_SUCCESS(hstatus)) {
hddLog(LOGE,
FL("sme_set_bpf_instructions failed(err=%d)"), hstatus);
goto fail;
}
EXIT();
if (bpf_set_offload->current_length)
vos_mem_free(bpf_set_offload->program);
vos_mem_free(bpf_set_offload);
return 0;
fail:
if (bpf_set_offload->current_length)
vos_mem_free(bpf_set_offload->program);
vos_mem_free(bpf_set_offload);
return ret_val;
}
/**
* wlan_hdd_cfg80211_bpf_offload() - Set/Reset to BPF Offload
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[BPF_MAX + 1];
int ret_val, packet_filter_subcmd;
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (!hdd_ctx->bpf_enabled) {
hddLog(LOGE, FL("BPF offload is not supported/enabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, BPF_MAX, data, data_len,
wlan_hdd_bpf_offload_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[BPF_SET_RESET]) {
hddLog(LOGE, FL("attr bpf set reset failed"));
return -EINVAL;
}
packet_filter_subcmd = nla_get_u32(tb[BPF_SET_RESET]);
if (packet_filter_subcmd == QCA_WLAN_GET_PACKET_FILTER)
return hdd_get_bpf_offload(hdd_ctx);
else
return hdd_set_reset_bpf_offload(hdd_ctx, tb,
pAdapter);
}
/**
* wlan_hdd_cfg80211_bpf_offload() - SSR Wrapper to BPF Offload
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_bpf_offload(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_init_bpf_completion() - Initialize the completion event for bpf
*
* Return: None
*/
void hdd_init_bpf_completion(void)
{
init_completion(&bpf_context.completion);
}
#undef BPF_INVALID
#undef BPF_SET_RESET
#undef BPF_VERSION
#undef BPF_ID
#undef BPF_PACKET_SIZE
#undef BPF_CURRENT_OFFSET
#undef BPF_PROGRAM
#undef BPF_MAX
/**
* __wlan_hdd_cfg80211_get_bus_size() - Get WMI Bus size
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* This function reads wmi max bus size and fill in the skb with
* NL attributes and send up the NL event.
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int ret_val;
struct sk_buff *skb;
uint32_t nl_buf_len;
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
hddLog(LOG1, "WMI Max Bus size: %d", hdd_ctx->wmi_max_len);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(hdd_ctx->wmi_max_len) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
hdd_ctx->wmi_max_len)) {
hddLog(LOGE, FL("nla put failure"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
EXIT();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_bus_size() - SSR Wrapper to Get Bus size
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_bus_size(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_wakelock_stats_rsp_callback()
*/
#define PARAM_TOTAL_CMD_EVENT_WAKE \
QCA_WLAN_VENDOR_ATTR_TOTAL_CMD_EVENT_WAKE
#define PARAM_CMD_EVENT_WAKE_CNT_PTR \
QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_PTR
#define PARAM_CMD_EVENT_WAKE_CNT_SZ \
QCA_WLAN_VENDOR_ATTR_CMD_EVENT_WAKE_CNT_SZ
#define PARAM_TOTAL_DRIVER_FW_LOCAL_WAKE \
QCA_WLAN_VENDOR_ATTR_TOTAL_DRIVER_FW_LOCAL_WAKE
#define PARAM_DRIVER_FW_LOCAL_WAKE_CNT_PTR \
QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_PTR
#define PARAM_DRIVER_FW_LOCAL_WAKE_CNT_SZ \
QCA_WLAN_VENDOR_ATTR_DRIVER_FW_LOCAL_WAKE_CNT_SZ
#define PARAM_TOTAL_RX_DATA_WAKE \
QCA_WLAN_VENDOR_ATTR_TOTAL_RX_DATA_WAKE
#define PARAM_RX_UNICAST_CNT \
QCA_WLAN_VENDOR_ATTR_RX_UNICAST_CNT
#define PARAM_RX_MULTICAST_CNT \
QCA_WLAN_VENDOR_ATTR_RX_MULTICAST_CNT
#define PARAM_RX_BROADCAST_CNT \
QCA_WLAN_VENDOR_ATTR_RX_BROADCAST_CNT
#define PARAM_ICMP_PKT \
QCA_WLAN_VENDOR_ATTR_ICMP_PKT
#define PARAM_ICMP6_PKT \
QCA_WLAN_VENDOR_ATTR_ICMP6_PKT
#define PARAM_ICMP6_RA \
QCA_WLAN_VENDOR_ATTR_ICMP6_RA
#define PARAM_ICMP6_NA \
QCA_WLAN_VENDOR_ATTR_ICMP6_NA
#define PARAM_ICMP6_NS \
QCA_WLAN_VENDOR_ATTR_ICMP6_NS
#define PARAM_ICMP4_RX_MULTICAST_CNT \
QCA_WLAN_VENDOR_ATTR_ICMP4_RX_MULTICAST_CNT
#define PARAM_ICMP6_RX_MULTICAST_CNT \
QCA_WLAN_VENDOR_ATTR_ICMP6_RX_MULTICAST_CNT
#define PARAM_OTHER_RX_MULTICAST_CNT \
QCA_WLAN_VENDOR_ATTR_OTHER_RX_MULTICAST_CNT
#define PARAM_RSSI_BREACH_CNT \
QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT
#define PARAM_LOW_RSSI_CNT \
QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT
#define PARAM_GSCAN_CNT \
QCA_WLAN_VENDOR_ATTR_GSCAN_CNT
#define PARAM_PNO_COMPLETE_CNT \
QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT
#define PARAM_PNO_MATCH_CNT \
QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT
/**
* hdd_send_wakelock_stats() - API to send wakelock stats
* @ctx: context to be passed to callback
* @data: data passed to callback
*
* This function is used to send wake lock stats to HAL layer
*
* Return: 0 on success, error number otherwise.
*/
static uint32_t hdd_send_wakelock_stats(hdd_context_t *hdd_ctx,
const struct sir_wake_lock_stats *data)
{
struct sk_buff *skb;
uint32_t nl_buf_len;
uint32_t total_rx_data_wake, rx_multicast_cnt;
uint32_t ipv6_rx_multicast_addr_cnt;
uint32_t icmpv6_cnt;
ENTER();
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len +=
QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX *
(NLMSG_HDRLEN + sizeof(uint32_t));
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, "wow_ucast_wake_up_count %d",
data->wow_ucast_wake_up_count);
hddLog(LOG1, "wow_bcast_wake_up_count %d",
data->wow_bcast_wake_up_count);
hddLog(LOG1, "wow_ipv4_mcast_wake_up_count %d",
data->wow_ipv4_mcast_wake_up_count);
hddLog(LOG1, "wow_ipv6_mcast_wake_up_count %d",
data->wow_ipv6_mcast_wake_up_count);
hddLog(LOG1, "wow_ipv6_mcast_ra_stats %d",
data->wow_ipv6_mcast_ra_stats);
hddLog(LOG1, "wow_ipv6_mcast_ns_stats %d",
data->wow_ipv6_mcast_ns_stats);
hddLog(LOG1, "wow_ipv6_mcast_na_stats %d",
data->wow_ipv6_mcast_na_stats);
hddLog(LOG1, "wow_icmpv4_count %d", data->wow_icmpv4_count);
hddLog(LOG1, "wow_icmpv6_count %d", data->wow_icmpv6_count);
hddLog(LOG1, "wow_rssi_breach_wake_up_count %d",
data->wow_rssi_breach_wake_up_count);
hddLog(LOG1, "wow_low_rssi_wake_up_count %d",
data->wow_low_rssi_wake_up_count);
hddLog(LOG1, "wow_gscan_wake_up_count %d",
data->wow_gscan_wake_up_count);
hddLog(LOG1, "wow_pno_complete_wake_up_count %d",
data->wow_pno_complete_wake_up_count);
hddLog(LOG1, "wow_pno_match_wake_up_count %d",
data->wow_pno_match_wake_up_count);
ipv6_rx_multicast_addr_cnt =
data->wow_ipv6_mcast_wake_up_count;
icmpv6_cnt =
data->wow_icmpv6_count;
rx_multicast_cnt =
data->wow_ipv4_mcast_wake_up_count +
ipv6_rx_multicast_addr_cnt;
total_rx_data_wake =
data->wow_ucast_wake_up_count +
data->wow_bcast_wake_up_count +
rx_multicast_cnt;
if (nla_put_u32(skb, PARAM_TOTAL_CMD_EVENT_WAKE, 0) ||
nla_put_u32(skb, PARAM_CMD_EVENT_WAKE_CNT_PTR, 0) ||
nla_put_u32(skb, PARAM_CMD_EVENT_WAKE_CNT_SZ, 0) ||
nla_put_u32(skb, PARAM_TOTAL_DRIVER_FW_LOCAL_WAKE, 0) ||
nla_put_u32(skb, PARAM_DRIVER_FW_LOCAL_WAKE_CNT_PTR, 0) ||
nla_put_u32(skb, PARAM_DRIVER_FW_LOCAL_WAKE_CNT_SZ, 0) ||
nla_put_u32(skb, PARAM_TOTAL_RX_DATA_WAKE,
total_rx_data_wake) ||
nla_put_u32(skb, PARAM_RX_UNICAST_CNT,
data->wow_ucast_wake_up_count) ||
nla_put_u32(skb, PARAM_RX_MULTICAST_CNT,
rx_multicast_cnt) ||
nla_put_u32(skb, PARAM_RX_BROADCAST_CNT,
data->wow_bcast_wake_up_count) ||
nla_put_u32(skb, PARAM_ICMP_PKT,
data->wow_icmpv4_count) ||
nla_put_u32(skb, PARAM_ICMP6_PKT,
icmpv6_cnt) ||
nla_put_u32(skb, PARAM_ICMP6_RA,
data->wow_ipv6_mcast_ra_stats) ||
nla_put_u32(skb, PARAM_ICMP6_NA,
data->wow_ipv6_mcast_na_stats) ||
nla_put_u32(skb, PARAM_ICMP6_NS,
data->wow_ipv6_mcast_ns_stats) ||
nla_put_u32(skb, PARAM_ICMP4_RX_MULTICAST_CNT,
data->wow_ipv4_mcast_wake_up_count) ||
nla_put_u32(skb, PARAM_ICMP6_RX_MULTICAST_CNT,
ipv6_rx_multicast_addr_cnt) ||
nla_put_u32(skb, PARAM_OTHER_RX_MULTICAST_CNT, 0) ||
nla_put_u32(skb, PARAM_RSSI_BREACH_CNT,
data->wow_rssi_breach_wake_up_count) ||
nla_put_u32(skb, PARAM_LOW_RSSI_CNT,
data->wow_low_rssi_wake_up_count) ||
nla_put_u32(skb, PARAM_GSCAN_CNT,
data->wow_gscan_wake_up_count) ||
nla_put_u32(skb, PARAM_PNO_COMPLETE_CNT,
data->wow_pno_complete_wake_up_count) ||
nla_put_u32(skb, PARAM_PNO_MATCH_CNT,
data->wow_pno_match_wake_up_count)) {
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
EXIT();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* WMA copies required data and invokes callback
* wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats.
*
* Return: 0 on success; error number otherwise.
*/
static int __wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
eHalStatus status;
int ret;
struct sir_wake_lock_stats wake_lock_stats;
VOS_STATUS vos_status;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
vos_status = wma_get_wakelock_stats(&wake_lock_stats);
if (vos_status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
FL("failed to get wakelock stats(err=%d)"), vos_status);
return -EINVAL;
}
ret = hdd_send_wakelock_stats(hdd_ctx,
&wake_lock_stats);
if (ret)
hddLog(LOGE, FL("Failed to post wake lock stats"));
EXIT();
return ret;
}
/**
* wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* WMA copies required data and invokes callback
* wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats.
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_wakelock_stats(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct nla_policy
wlan_hdd_set_acs_dfs_config_policy[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1] =
{
[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT] = {.type = NLA_U8 },
};
/**
* __wlan_hdd_cfg80211_acs_dfs_mode() - set ACS DFS mode and channel
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function parses the incoming NL vendor command data attributes and
* updates the SAP context about channel_hint and DFS mode.
* If channel_hint is set, SAP will choose that channel
* as operating channel.
*
* If DFS mode is enabled, driver will include DFS channels
* in ACS else driver will skip DFS channels.
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1];
int ret;
struct acs_dfs_policy *acs_policy;
int mode = DFS_MODE_NONE;
int channel_hint = 0;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX,
data, data_len,
wlan_hdd_set_acs_dfs_config_policy)) {
hddLog(LOGE, FL("invalid attr"));
return -EINVAL;
}
acs_policy = &hdd_ctx->acs_policy;
/*
* SCM sends this attribute to restrict SAP from choosing
* DFS channels from ACS.
*/
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE])
mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]);
if (!IS_DFS_MODE_VALID(mode)) {
hddLog(LOGE, FL("attr acs dfs mode is not valid"));
return -EINVAL;
}
acs_policy->acs_dfs_mode = mode;
/*
* SCM sends this attribute to provide an active channel,
* to skip redundant ACS between drivers, and save driver start up time
*/
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT])
channel_hint = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]);
if (!IS_CHANNEL_VALID(channel_hint)) {
hddLog(LOGE, FL("acs channel is not valid"));
return -EINVAL;
}
acs_policy->acs_channel = channel_hint;
return 0;
}
/**
* wlan_hdd_cfg80211_acs_dfs_mode() - Wrapper to set ACS DFS mode
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* This function parses the incoming NL vendor command data attributes and
* updates the SAP context about channel_hint and DFS mode.
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_acs_dfs_mode(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_acs_dfs_mode(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* wlan_hdd_get_sta_roam_dfs_mode() - get sta roam dfs mode policy
* @mode : cfg80211 dfs mode
*
* Return: return csr sta roam dfs mode else return NONE
*/
static enum sta_roam_policy_dfs_mode wlan_hdd_get_sta_roam_dfs_mode(
enum dfs_mode mode)
{
switch (mode) {
case DFS_MODE_ENABLE:
return CSR_STA_ROAM_POLICY_DFS_ENABLED;
break;
case DFS_MODE_DISABLE:
return CSR_STA_ROAM_POLICY_DFS_DISABLED;
break;
case DFS_MODE_DEPRIORITIZE:
return CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE;
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("STA Roam policy dfs mode is NONE"));
return CSR_STA_ROAM_POLICY_NONE;
}
}
/*
* hdd_get_sap_operating_channel: Get current operating channel
* for sap.
* @hdd_ctx: hdd context
*
* Return : Corresponding band for SAP operating channel
*/
uint8_t hdd_get_sap_operating_channel(hdd_context_t *hdd_ctx) {
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
VOS_STATUS status;
hdd_adapter_t *adapter;
uint8_t operating_channel = 0;
uint8_t sap_operating_band = 0;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if (!(adapter && (WLAN_HDD_SOFTAP == adapter->device_mode))) {
status = hdd_get_next_adapter(hdd_ctx, adapter_node,
&next);
adapter_node = next;
continue;
}
operating_channel = adapter->sessionCtx.ap.operatingChannel;
if (IS_24G_CH(operating_channel))
sap_operating_band = eCSR_BAND_24;
else if(IS_5G_CH(operating_channel))
sap_operating_band = eCSR_BAND_5G;
else sap_operating_band = eCSR_BAND_ALL;
status = hdd_get_next_adapter(hdd_ctx, adapter_node,
&next);
adapter_node = next;
}
return sap_operating_band;
}
static const struct nla_policy
wlan_hdd_set_sta_roam_config_policy[
QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1] =
{
[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL] = {.type = NLA_U8 },
};
/**
* __wlan_hdd_cfg80211_sta_roam_policy() - Set params to restrict scan channels
* for station connection or roaming.
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe
* channels needs to be skipped in scanning or not.
* If dfs_mode is disabled, driver will not scan DFS channels.
* If skip_unsafe_channels is set, driver will skip unsafe channels
* in Scanning.
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[
QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1];
int ret;
enum sta_roam_policy_dfs_mode sta_roam_dfs_mode;
enum dfs_mode mode = DFS_MODE_NONE;
bool skip_unsafe_channels = false;
eHalStatus status;
uint8_t sap_operating_band = 0;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX,
data, data_len,
wlan_hdd_set_sta_roam_config_policy)) {
hddLog(LOGE, FL("invalid attr"));
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE])
mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]);
if (!IS_DFS_MODE_VALID(mode)) {
hddLog(LOGE, FL("attr sta roam dfs mode policy is not valid"));
return -EINVAL;
}
sta_roam_dfs_mode = wlan_hdd_get_sta_roam_dfs_mode(mode);
if (tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL])
skip_unsafe_channels = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]);
sap_operating_band = hdd_get_sap_operating_channel(hdd_ctx);
status = sme_update_sta_roam_policy(hdd_ctx->hHal, sta_roam_dfs_mode,
skip_unsafe_channels, adapter->sessionId,
sap_operating_band);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_update_sta_roam_policy (err=%d)"), status);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_sta_roam_policy() - Wrapper to restrict scan channels,
* connection and roaming for station.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe
* channels needs to be skipped in scanning or not.
* If dfs_mode is disabled, driver will not scan DFS channels.
* If skip_unsafe_channels is set, driver will skip unsafe channels
* in Scanning.
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_sta_roam_policy(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_WLAN_CH_AVOID
static int hdd_validate_avoid_freq_chanlist(hdd_context_t *hdd_ctx,
tHddAvoidFreqList *channel_list)
{
unsigned int range_idx, ch_idx;
unsigned int unsafe_channel_index, unsafe_channel_count = 0;
bool ch_found = false;
unsafe_channel_count = VOS_MIN((uint16_t)hdd_ctx->unsafe_channel_count,
(uint16_t)NUM_20MHZ_RF_CHANNELS);
for (range_idx = 0; range_idx < channel_list->avoidFreqRangeCount;
range_idx++) {
if ((channel_list->avoidFreqRange[range_idx].startFreq <
VOS_24_GHZ_CHANNEL_1) ||
(channel_list->avoidFreqRange[range_idx].endFreq >
VOS_5_GHZ_CHANNEL_165) ||
(channel_list->avoidFreqRange[range_idx].startFreq >
channel_list->avoidFreqRange[range_idx].endFreq))
continue;
for (ch_idx = channel_list->avoidFreqRange[range_idx].startFreq;
ch_idx <= channel_list->avoidFreqRange[range_idx].endFreq;
ch_idx++) {
for (unsafe_channel_index = 0;
unsafe_channel_index < unsafe_channel_count;
unsafe_channel_index++) {
if (ch_idx ==
hdd_ctx->unsafe_channel_list[
unsafe_channel_index]) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Duplicate channel %d"),
ch_idx);
ch_found = true;
break;
}
}
if (!ch_found) {
hdd_ctx->unsafe_channel_list[
unsafe_channel_count++] = ch_idx;
}
ch_found = false;
}
}
return unsafe_channel_count;
}
/**
* __wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP
* is on unsafe channel.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already
* on any of unsafe channels.
* If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap
* will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart.
*
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int ret;
int unsafe_channel_index;
tHddAvoidFreqList *channel_list;
tVOS_CON_MODE curr_mode;
ENTER();
curr_mode = hdd_get_conparam();
if (VOS_FTM_MODE == curr_mode ||
VOS_MONITOR_MODE == curr_mode) {
hddLog(LOGE, FL("Command not allowed in FTM/MONITOR mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
channel_list = (tHddAvoidFreqList *)data;
if (!channel_list) {
hddLog(LOGE, FL("Avoid frequency channel list empty"));
return -EINVAL;
}
vos_get_wlan_unsafe_channel(hdd_ctx->unsafe_channel_list,
&(hdd_ctx->unsafe_channel_count),
sizeof(hdd_ctx->unsafe_channel_list));
hdd_ctx->unsafe_channel_count =
hdd_validate_avoid_freq_chanlist(hdd_ctx,
channel_list);
vos_set_wlan_unsafe_channel(hdd_ctx->unsafe_channel_list,
hdd_ctx->unsafe_channel_count);
for (unsafe_channel_index = 0;
unsafe_channel_index < hdd_ctx->unsafe_channel_count;
unsafe_channel_index++) {
hddLog(LOGE, FL("Channel %d is not safe. "),
hdd_ctx->unsafe_channel_list[unsafe_channel_index]);
}
hdd_unsafe_channel_restart_sap(hdd_ctx);
return 0;
}
/**
* wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP
* is on unsafe channel.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already
* on any of unsafe channels.
* If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap
* will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart.
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_avoid_freq(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
static const struct nla_policy
wlan_hdd_sap_config_policy[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1] =
{
[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL] = {.type = NLA_U8 },
};
/*
* __wlan_hdd_cfg80211_sap_configuration_set() - ask driver to restart SAP if
SAP
* is on unsafe channel.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to
* driver.
* QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and
* will initiate restart of sap.
*
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *ndev = wdev->netdev;
hdd_adapter_t *hostapd_adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1];
uint8_t config_channel = 0;
hdd_ap_ctx_t *ap_ctx;
int ret;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
if (!test_bit(SOFTAP_BSS_STARTED, &hostapd_adapter->event_flags)) {
hddLog(LOGE, FL("SAP is not started yet. Restart sap will be invalid"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX,
data, data_len,
wlan_hdd_sap_config_policy)) {
hddLog(LOGE, FL("invalid attr"));
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL])
config_channel =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]);
if (!IS_CHANNEL_VALID(config_channel)) {
hddLog(LOGE, FL("Channel %d is not valid to restart SAP"),
config_channel);
return -ENOTSUPP;
}
ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter);
ap_ctx->sapConfig.channel = config_channel;
sme_SelectCBMode(hdd_ctx->hHal,
ap_ctx->sapConfig.SapHw_mode,
ap_ctx->sapConfig.channel,
ap_ctx->sapConfig.sec_ch,
&ap_ctx->sapConfig.vht_channel_width,
ap_ctx->sapConfig.ch_width_orig);
wlan_hdd_restart_sap(hostapd_adapter);
return 0;
}
/**
* wlan_hdd_cfg80211_sap_configuration_set() - sap configuration vendor command
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to
* driver.
* QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and
* will initiate restart of sap.
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_sap_configuration_set(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_get_station_cmd()
*/
#define STATION_INVALID \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INVALID
#define STATION_INFO \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO
#define STATION_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_ASSOC_FAIL_REASON
#define STATION_REMOTE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_REMOTE
#define STATION_MAX \
QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX
static const struct nla_policy
hdd_get_station_policy[STATION_MAX + 1] = {
[STATION_INFO] = {.type = NLA_FLAG},
[STATION_ASSOC_FAIL_REASON] = {.type = NLA_FLAG},
};
/* define short names for get station info attributes */
#define LINK_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_LINK_STANDARD_NL80211_ATTR
#define AP_INFO_STANDARD_NL80211_ATTR \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_STANDARD_NL80211_ATTR
#define INFO_ROAM_COUNT \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ROAM_COUNT
#define INFO_AKM \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AKM
#define WLAN802_11_MODE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_802_11_MODE
#define AP_INFO_HS20_INDICATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_AP_HS20_INDICATION
#define HT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_HT_OPERATION
#define VHT_OPERATION \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_VHT_OPERATION
#define INFO_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_ASSOC_FAIL_REASON
#define REMOTE_MAX_PHY_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_MAX_PHY_RATE
#define REMOTE_TX_PACKETS \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_PACKETS
#define REMOTE_TX_BYTES \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_BYTES
#define REMOTE_RX_PACKETS \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_PACKETS
#define REMOTE_RX_BYTES \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_BYTES
#define REMOTE_LAST_TX_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_TX_RATE
#define REMOTE_LAST_RX_RATE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_LAST_RX_RATE
#define REMOTE_WMM \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_WMM
#define REMOTE_SUPPORTED_MODE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SUPPORTED_MODE
#define REMOTE_AMPDU \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_AMPDU
#define REMOTE_TX_STBC \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_TX_STBC
#define REMOTE_RX_STBC \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_RX_STBC
#define REMOTE_CH_WIDTH\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_CH_WIDTH
#define REMOTE_SGI_ENABLE\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_REMOTE_SGI_ENABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#define REMOTE_PAD\
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_PAD
#endif
/**
* hdd_get_station_assoc_fail() - Handle get station assoc fail
* @hdd_ctx: HDD context within host driver
* @wdev: wireless device
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION_ASSOC_FAIL.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int hdd_get_station_assoc_fail(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
struct sk_buff *skb = NULL;
uint32_t nl_buf_len;
hdd_station_ctx_t *hdd_sta_ctx;
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += sizeof(uint32_t);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (nla_put_u32(skb, INFO_ASSOC_FAIL_REASON,
hdd_sta_ctx->conn_info.assoc_status_code)) {
hddLog(LOGE, FL("put fail assoc status code"));
goto fail;
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
#ifdef FEATURE_WLAN_ESE
/**
* hdd_check_cckm_auth_type() - check cckm auth type
* @auth_type: csr auth type
*
* Return: auth type
*/
static int hdd_check_cckm_auth_type(uint32_t auth_type)
{
uint32_t ret_val = QCA_WLAN_AUTH_TYPE_INVALID;
if (auth_type == eCSR_AUTH_TYPE_CCKM_WPA)
ret_val = QCA_WLAN_AUTH_TYPE_CCKM_WPA;
else if (auth_type == eCSR_AUTH_TYPE_CCKM_RSN)
ret_val = QCA_WLAN_AUTH_TYPE_CCKM_RSN;
return ret_val;
}
#else
static int hdd_check_cckm_auth_type(uint32_t auth_type)
{
return QCA_WLAN_AUTH_TYPE_INVALID;
}
#endif
/**
* hdd_map_auth_type() - transform auth type specific to
* vendor command
* @auth_type: csr auth type
*
* Return: Success(0) or reason code for failure
*/
static int hdd_convert_auth_type(uint32_t auth_type)
{
uint32_t ret_val;
ret_val = hdd_check_cckm_auth_type(auth_type);
if (ret_val != QCA_WLAN_AUTH_TYPE_INVALID)
return ret_val;
switch (auth_type) {
case eCSR_AUTH_TYPE_OPEN_SYSTEM:
ret_val = QCA_WLAN_AUTH_TYPE_OPEN;
break;
case eCSR_AUTH_TYPE_SHARED_KEY:
ret_val = QCA_WLAN_AUTH_TYPE_SHARED;
break;
case eCSR_AUTH_TYPE_WPA:
ret_val = QCA_WLAN_AUTH_TYPE_WPA;
break;
case eCSR_AUTH_TYPE_WPA_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_WPA_PSK;
break;
case eCSR_AUTH_TYPE_AUTOSWITCH:
ret_val = QCA_WLAN_AUTH_TYPE_AUTOSWITCH;
break;
case eCSR_AUTH_TYPE_WPA_NONE:
ret_val = QCA_WLAN_AUTH_TYPE_WPA_NONE;
break;
case eCSR_AUTH_TYPE_RSN:
ret_val = QCA_WLAN_AUTH_TYPE_RSN;
break;
case eCSR_AUTH_TYPE_RSN_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_RSN_PSK;
break;
case eCSR_AUTH_TYPE_FT_RSN:
ret_val = QCA_WLAN_AUTH_TYPE_FT;
break;
case eCSR_AUTH_TYPE_FT_RSN_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_FT_PSK;
break;
case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE:
ret_val = QCA_WLAN_AUTH_TYPE_WAI;
break;
case eCSR_AUTH_TYPE_WAPI_WAI_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_WAI_PSK;
break;
case eCSR_AUTH_TYPE_RSN_PSK_SHA256:
ret_val = QCA_WLAN_AUTH_TYPE_SHA256_PSK;
break;
case eCSR_AUTH_TYPE_RSN_8021X_SHA256:
ret_val = QCA_WLAN_AUTH_TYPE_SHA256;
break;
case eCSR_NUM_OF_SUPPORT_AUTH_TYPE:
case eCSR_AUTH_TYPE_FAILED:
case eCSR_AUTH_TYPE_NONE:
default:
ret_val = QCA_WLAN_AUTH_TYPE_INVALID;
break;
}
return ret_val;
}
/**
* hdd_map_dot_11_mode() - transform dot11mode type specific to
* vendor command
* @dot11mode: dot11mode
*
* Return: Success(0) or reason code for failure
*/
static int hdd_convert_dot11mode(uint32_t dot11mode)
{
uint32_t ret_val;
switch (dot11mode) {
case eCSR_CFG_DOT11_MODE_11A:
ret_val = QCA_WLAN_802_11_MODE_11A;
break;
case eCSR_CFG_DOT11_MODE_11B:
ret_val = QCA_WLAN_802_11_MODE_11B;
break;
case eCSR_CFG_DOT11_MODE_11G:
ret_val = QCA_WLAN_802_11_MODE_11G;
break;
case eCSR_CFG_DOT11_MODE_11N:
ret_val = QCA_WLAN_802_11_MODE_11N;
break;
case eCSR_CFG_DOT11_MODE_11AC:
ret_val = QCA_WLAN_802_11_MODE_11AC;
break;
case eCSR_CFG_DOT11_MODE_AUTO:
case eCSR_CFG_DOT11_MODE_ABG:
default:
ret_val = QCA_WLAN_802_11_MODE_INVALID;
}
return ret_val;
}
/**
* hdd_add_tx_bitrate() - add tx bitrate attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_tx_bitrate(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx,
int idx)
{
struct nlattr *nla_attr;
uint32_t bitrate, bitrate_compat;
struct rate_info txrate;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
txrate.flags = hdd_sta_ctx->conn_info.txrate.flags;
txrate.mcs = hdd_sta_ctx->conn_info.txrate.mcs;
txrate.legacy = hdd_sta_ctx->conn_info.txrate.legacy;
txrate.nss = hdd_sta_ctx->conn_info.txrate.nss;
bitrate = cfg80211_calculate_bitrate(&txrate);
hdd_sta_ctx->conn_info.txrate.flags = txrate.flags;
hdd_sta_ctx->conn_info.txrate.mcs = txrate.mcs;
hdd_sta_ctx->conn_info.txrate.legacy = txrate.legacy;
hdd_sta_ctx->conn_info.txrate.nss = txrate.nss;
/* report 16-bit bitrate only if we can */
bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
if (bitrate > 0 &&
nla_put_u32(skb, NL80211_RATE_INFO_BITRATE32, bitrate)) {
hddLog(LOGE, FL("put fail bitrate32"));
goto fail;
}
if (bitrate_compat > 0 &&
nla_put_u16(skb, NL80211_RATE_INFO_BITRATE, bitrate_compat)) {
hddLog(LOGE, FL("put fail bitrate"));
goto fail;
}
if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS,
hdd_sta_ctx->conn_info.txrate.nss)) {
hddLog(LOGE, FL("put fail nss"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_sta_info() - add station info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_sta_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL,
(hdd_sta_ctx->conn_info.signal + 100))) {
hddLog(LOGE, FL("put fail signal"));
goto fail;
}
if (hdd_add_tx_bitrate(skb, hdd_sta_ctx, NL80211_STA_INFO_TX_BITRATE))
goto fail;
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_survey_info() - add survey info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_survey_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY,
hdd_sta_ctx->conn_info.freq) ||
nla_put_u8(skb, NL80211_SURVEY_INFO_NOISE,
(hdd_sta_ctx->conn_info.noise + 100))) {
hddLog(LOGE, FL("put fail noise"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_link_standard_info() - add link info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_add_link_standard_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put(skb,
NL80211_ATTR_SSID,
hdd_sta_ctx->conn_info.last_ssid.SSID.length,
hdd_sta_ctx->conn_info.last_ssid.SSID.ssId)) {
hddLog(LOGE, FL("put fail ssid"));
goto fail;
}
if (hdd_add_survey_info(skb, hdd_sta_ctx, NL80211_ATTR_SURVEY_INFO))
goto fail;
if (hdd_add_sta_info(skb, hdd_sta_ctx, NL80211_ATTR_STA_INFO))
goto fail;
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_ap_standard_info() - add ap info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_add_ap_standard_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (hdd_sta_ctx->conn_info.conn_flag.vht_present)
if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY,
sizeof(hdd_sta_ctx->conn_info.vht_caps),
&hdd_sta_ctx->conn_info.vht_caps)) {
hddLog(LOGE, FL("put fail vht cap"));
goto fail;
}
if (hdd_sta_ctx->conn_info.conn_flag.ht_present)
if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY,
sizeof(hdd_sta_ctx->conn_info.ht_caps),
&hdd_sta_ctx->conn_info.ht_caps)) {
hddLog(LOGE, FL("put fail ht cap"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_get_station_info() - send BSS information to supplicant
* @hdd_ctx: pointer to hdd context
* @adapter: pointer to adapter
*
* Return: 0 if success else error status
*/
static int hdd_get_station_info(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
struct sk_buff *skb = NULL;
uint8_t *tmp_hs20 = NULL;
uint32_t nl_buf_len;
hdd_station_ctx_t *hdd_sta_ctx;
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += sizeof(hdd_sta_ctx->conn_info.last_ssid.SSID.length) +
sizeof(hdd_sta_ctx->conn_info.freq) +
sizeof(hdd_sta_ctx->conn_info.noise) +
sizeof(hdd_sta_ctx->conn_info.signal) +
(sizeof(uint32_t) * 2) +
sizeof(hdd_sta_ctx->conn_info.txrate.nss) +
sizeof(hdd_sta_ctx->conn_info.roam_count) +
sizeof(hdd_sta_ctx->conn_info.last_auth_type) +
sizeof(hdd_sta_ctx->conn_info.dot11Mode);
if (hdd_sta_ctx->conn_info.conn_flag.vht_present)
nl_buf_len += sizeof(hdd_sta_ctx->conn_info.vht_caps);
if (hdd_sta_ctx->conn_info.conn_flag.ht_present)
nl_buf_len += sizeof(hdd_sta_ctx->conn_info.ht_caps);
if (hdd_sta_ctx->conn_info.conn_flag.hs20_present) {
tmp_hs20 = (uint8_t *)&(hdd_sta_ctx->conn_info.hs20vendor_ie);
nl_buf_len += (sizeof(hdd_sta_ctx->conn_info.hs20vendor_ie) -
1);
}
if (hdd_sta_ctx->conn_info.conn_flag.ht_op_present)
nl_buf_len += sizeof(hdd_sta_ctx->conn_info.ht_operation);
if (hdd_sta_ctx->conn_info.conn_flag.vht_op_present)
nl_buf_len += sizeof(hdd_sta_ctx->conn_info.vht_operation);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
if (hdd_add_link_standard_info(skb, hdd_sta_ctx,
LINK_INFO_STANDARD_NL80211_ATTR)) {
hddLog(LOGE, FL("put fail link standard info"));
goto fail;
}
if (hdd_add_ap_standard_info(skb, hdd_sta_ctx,
AP_INFO_STANDARD_NL80211_ATTR)) {
hddLog(LOGE, FL("put fail ap standard info"));
goto fail;
}
if (nla_put_u32(skb, INFO_ROAM_COUNT,
hdd_sta_ctx->conn_info.roam_count) ||
nla_put_u32(skb, INFO_AKM,
hdd_convert_auth_type(
hdd_sta_ctx->conn_info.last_auth_type)) ||
nla_put_u32(skb, WLAN802_11_MODE,
hdd_convert_dot11mode(
hdd_sta_ctx->conn_info.dot11Mode))) {
hddLog(LOGE, FL("put fail roam_count, etc."));
goto fail;
}
if (hdd_sta_ctx->conn_info.conn_flag.ht_op_present)
if (nla_put(skb, HT_OPERATION,
(sizeof(hdd_sta_ctx->conn_info.ht_operation)),
&hdd_sta_ctx->conn_info.ht_operation)) {
hddLog(LOGE, FL("put fail HT oper"));
goto fail;
}
if (hdd_sta_ctx->conn_info.conn_flag.vht_op_present)
if (nla_put(skb, VHT_OPERATION,
(sizeof(hdd_sta_ctx->conn_info.vht_operation)),
&hdd_sta_ctx->conn_info.vht_operation)) {
hddLog(LOGE, FL("put fail VHT oper"));
goto fail;
}
if (hdd_sta_ctx->conn_info.conn_flag.hs20_present)
if (nla_put(skb, AP_INFO_HS20_INDICATION,
(sizeof(hdd_sta_ctx->conn_info.hs20vendor_ie) - 1),
tmp_hs20 + 1)) {
hddLog(LOGE, FL("put fail HS20 IND"));
goto fail;
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* hdd_get_peer_txrx_rate_cb() - get station's txrx rate callback
* @peer_info: pointer of peer information
* @context: get peer info callback context
*
* This function fill txrx rate information to aStaInfo[staid] of hostapd
* adapter
*/
static void hdd_get_peer_txrx_rate_cb(struct sir_peer_info_ext_resp *peer_info,
void *context)
{
struct statsContext *get_txrx_rate_context;
struct sir_peer_info_ext *txrx_rate = NULL;
hdd_adapter_t *adapter;
uint8_t staid;
if ((NULL == peer_info) || (NULL == context)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Bad param, peer_info [%pK] context [%pK]",
__func__, peer_info, context);
return;
}
spin_lock(&hdd_context_lock);
/*
* there is a race condition that exists between this callback
* function and the caller since the caller could time out either
* before or while this code is executing. we use a spinlock to
* serialize these actions
*/
get_txrx_rate_context = context;
if (PEER_INFO_CONTEXT_MAGIC !=
get_txrx_rate_context->magic) {
/*
* the caller presumably timed out so there is nothing
* we can do
*/
spin_unlock(&hdd_context_lock);
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Invalid context, magic [%08x]",
__func__,
get_txrx_rate_context->magic);
return;
}
if (!peer_info->count) {
spin_unlock(&hdd_context_lock);
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Fail to get remote peer info"));
return;
}
adapter = get_txrx_rate_context->pAdapter;
txrx_rate = peer_info->info;
if (VOS_STATUS_SUCCESS != hdd_softap_GetStaId(adapter,
(v_MACADDR_t *)txrx_rate->peer_macaddr,
&staid)) {
spin_unlock(&hdd_context_lock);
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Station MAC address does not matching",
__func__);
return;
}
adapter->aStaInfo[staid].tx_rate = txrx_rate->tx_rate;
adapter->aStaInfo[staid].rx_rate = txrx_rate->rx_rate;
hddLog(VOS_TRACE_LEVEL_INFO, "%s txrate %x rxrate %x\n",
__func__,
adapter->aStaInfo[staid].tx_rate,
adapter->aStaInfo[staid].rx_rate);
get_txrx_rate_context->magic = 0;
/* notify the caller */
complete(&get_txrx_rate_context->completion);
/* serialization is complete */
spin_unlock(&hdd_context_lock);
if (txrx_rate)
hddLog(VOS_TRACE_LEVEL_INFO, "%s %pM tx rate %u rx rate %u",
__func__,
txrx_rate->peer_macaddr,
txrx_rate->tx_rate,
txrx_rate->rx_rate);
}
/**
* wlan_hdd_get_peer_txrx_rate() - get station's txrx rate
* @adapter: hostapd interface
* @macaddress: mac address of requested peer
*
* This function call sme_get_peer_info_ext to get txrx rate
*
* Return: 0 on success, otherwise error value
*/
static int wlan_hdd_get_txrx_rate(hdd_adapter_t *adapter,
v_MACADDR_t macaddress)
{
eHalStatus hstatus;
int ret;
struct statsContext context;
struct sir_peer_info_ext_req txrx_rate_req;
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: pAdapter is NULL",
__func__);
return -EFAULT;
}
init_completion(&context.completion);
context.magic = PEER_INFO_CONTEXT_MAGIC;
context.pAdapter = adapter;
vos_mem_copy(&(txrx_rate_req.peer_macaddr), &macaddress,
VOS_MAC_ADDR_SIZE);
txrx_rate_req.sessionid = adapter->sessionId;
txrx_rate_req.reset_after_request = 0;
hstatus = sme_get_peer_info_ext(WLAN_HDD_GET_HAL_CTX(adapter),
&txrx_rate_req,
&context,
hdd_get_peer_txrx_rate_cb);
if (eHAL_STATUS_SUCCESS != hstatus) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unable to retrieve statistics for txrx_rate",
__func__);
ret = -EFAULT;
} else {
if (!wait_for_completion_timeout(&context.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_STATS))) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME timed out while retrieving txrx_rate",
__func__);
ret = -EFAULT;
} else {
ret = 0;
}
}
/*
* either we never sent a request, we sent a request and received a
* response or we sent a request and timed out. if we never sent a
* request or if we sent a request and got a response, we want to
* clear the magic out of paranoia. if we timed out there is a
* race condition such that the callback function could be
* executing at the same time we are. of primary concern is if the
* callback function had already verified the "magic" but had not
* yet set the completion variable when a timeout occurred. we
* serialize these activities by invalidating the magic while
* holding a shared spinlock which will cause us to block if the
* callback is currently executing
*/
spin_lock(&hdd_context_lock);
context.magic = 0;
spin_unlock(&hdd_context_lock);
return ret;
}
/**
* hdd_get_stainfo() - get stainfo for the specified peer
* @adapter: hostapd interface
* @mac_addr: mac address of requested peer
*
* This function find the stainfo for the peer with mac_addr
*
* Return: stainfo if found, NULL if not found
*/
static hdd_station_info_t *hdd_get_stainfo(hdd_adapter_t *adapter,
v_MACADDR_t mac_addr)
{
hdd_station_info_t *stainfo = NULL;
int i;
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
if (vos_mem_compare(&adapter->aStaInfo[i].macAddrSTA,
&mac_addr,
VOS_MAC_ADDR_SIZE))
stainfo = &adapter->aStaInfo[i];
}
return stainfo;
}
/**
* hdd_get_station_remote() - get remote peer's info
* @hdd_ctx: hdd context
* @adapter: hostapd interface
* @mac_addr: mac address of requested peer
*
* This function collect and indicate the remote peer's info
*
* Return: 0 on success, otherwise error value
*/
static int hdd_get_station_remote(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
v_MACADDR_t mac_addr)
{
hdd_station_info_t *stainfo = hdd_get_stainfo(adapter, mac_addr);
struct sk_buff *skb = NULL;
uint32_t nl_buf_len;
bool txrx_rate = true;
if (!stainfo) {
hddLog(LOGE, FL("peer" MAC_ADDRESS_STR "not found"),
MAC_ADDR_ARRAY(mac_addr.bytes));
goto fail;
}
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(stainfo->max_phy_rate) + NLA_HDRLEN) +
(sizeof(stainfo->tx_packets) + NLA_HDRLEN) +
(sizeof(stainfo->tx_bytes) + NLA_HDRLEN) +
(sizeof(stainfo->rx_packets) + NLA_HDRLEN) +
(sizeof(stainfo->rx_bytes) + NLA_HDRLEN) +
(sizeof(stainfo->isQosEnabled) + NLA_HDRLEN) +
(sizeof(stainfo->mode) + NLA_HDRLEN);
if (!hdd_ctx->cfg_ini->sap_get_peer_info ||
wlan_hdd_get_txrx_rate(adapter, mac_addr)) {
hddLog(LOGE, FL("fail to get tx/rx rate"));
txrx_rate = false;
} else {
nl_buf_len += (sizeof(stainfo->tx_rate) + NLA_HDRLEN) +
(sizeof(stainfo->rx_rate) + NLA_HDRLEN);
}
/* below info is only valid for HT/VHT mode */
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY)
nl_buf_len += (sizeof(stainfo->ampdu) + NLA_HDRLEN) +
(sizeof(stainfo->tx_stbc) + NLA_HDRLEN) +
(sizeof(stainfo->rx_stbc) + NLA_HDRLEN) +
(sizeof(stainfo->ch_width) + NLA_HDRLEN) +
(sizeof(stainfo->sgi_enable) + NLA_HDRLEN);
hddLog(VOS_TRACE_LEVEL_INFO, FL("buflen %d hdrlen %d"),
nl_buf_len, NLMSG_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
nl_buf_len);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
goto fail;
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("stainfo"));
hddLog(VOS_TRACE_LEVEL_INFO,
FL("maxrate %x tx_pkts %x tx_bytes %llx"),
stainfo->max_phy_rate, stainfo->tx_packets,
stainfo->tx_bytes);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("rx_pkts %x rx_bytes %llx mode %x"),
stainfo->rx_packets, stainfo->rx_bytes,
stainfo->mode);
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ampdu %d tx_stbc %d rx_stbc %d"),
stainfo->ampdu, stainfo->tx_stbc,
stainfo->rx_stbc);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("wmm %d chwidth %d sgi %d"),
stainfo->isQosEnabled,
stainfo->ch_width,
stainfo->sgi_enable);
}
if (nla_put_u32(skb, REMOTE_MAX_PHY_RATE, stainfo->max_phy_rate) ||
nla_put_u32(skb, REMOTE_TX_PACKETS, stainfo->tx_packets) ||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
nla_put_u64_64bit(skb, REMOTE_TX_BYTES, stainfo->tx_bytes,
REMOTE_PAD) ||
#else
nla_put_u64(skb, REMOTE_TX_BYTES, stainfo->tx_bytes) ||
#endif
nla_put_u32(skb, REMOTE_RX_PACKETS, stainfo->rx_packets) ||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
nla_put_u64_64bit(skb, REMOTE_RX_BYTES, stainfo->rx_bytes,
REMOTE_PAD) ||
#else
nla_put_u64(skb, REMOTE_RX_BYTES, stainfo->rx_bytes) ||
#endif
nla_put_u8(skb, REMOTE_WMM, stainfo->isQosEnabled) ||
nla_put_u8(skb, REMOTE_SUPPORTED_MODE, stainfo->mode)) {
hddLog(LOGE, FL("put fail"));
goto fail;
}
if (txrx_rate) {
if (nla_put_u32(skb, REMOTE_LAST_TX_RATE, stainfo->tx_rate) ||
nla_put_u32(skb, REMOTE_LAST_RX_RATE, stainfo->rx_rate)) {
hddLog(LOGE, FL("put fail"));
goto fail;
} else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("tx_rate %x rx_rate %x"),
stainfo->tx_rate, stainfo->rx_rate);
}
}
if (stainfo->mode > SIR_SME_PHY_MODE_LEGACY) {
if (nla_put_u8(skb, REMOTE_AMPDU, stainfo->ampdu) ||
nla_put_u8(skb, REMOTE_TX_STBC, stainfo->tx_stbc) ||
nla_put_u8(skb, REMOTE_RX_STBC, stainfo->rx_stbc) ||
nla_put_u8(skb, REMOTE_CH_WIDTH, stainfo->ch_width) ||
nla_put_u8(skb, REMOTE_SGI_ENABLE, stainfo->sgi_enable)) {
hddLog(LOGE, FL("put fail"));
goto fail;
}
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int
__hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + 1];
int32_t status;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
status = -EPERM;
goto out;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
goto out;
status = nla_parse(tb, STATION_MAX, data, data_len,
hdd_get_station_policy);
if (status) {
hddLog(LOGE, FL("Invalid ATTR"));
goto out;
}
/* Parse and fetch Command Type*/
if (tb[STATION_INFO]) {
status = hdd_get_station_info(hdd_ctx, adapter);
} else if (tb[STATION_ASSOC_FAIL_REASON]) {
status = hdd_get_station_assoc_fail(hdd_ctx, adapter);
} else if (tb[STATION_REMOTE]) {
v_MACADDR_t mac_addr;
if (adapter->device_mode != WLAN_HDD_SOFTAP) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("invalid device_mode:%d"),
adapter->device_mode);
status = -EINVAL;
goto out;
}
memcpy(mac_addr.bytes,
nla_data(tb[STATION_REMOTE]),
VOS_MAC_ADDRESS_LEN);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("STATION_REMOTE "MAC_ADDRESS_STR""),
MAC_ADDR_ARRAY(mac_addr.bytes));
status = hdd_get_station_remote(hdd_ctx, adapter, mac_addr);
} else {
hddLog(LOGE, FL("get station info cmd type failed"));
status = -EINVAL;
goto out;
}
EXIT();
out:
return status;
}
/**
* wlan_hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_cfg80211_get_station_cmd(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* undef short names defined for get station command
* used by __wlan_hdd_cfg80211_get_station_cmd()
*/
#undef STATION_INVALID
#undef STATION_INFO
#undef STATION_ASSOC_FAIL_REASON
#undef STATION_REMOTE
#undef STATION_MAX
#undef LINK_INFO_STANDARD_NL80211_ATTR
#undef AP_INFO_STANDARD_NL80211_ATTR
#undef INFO_ROAM_COUNT
#undef INFO_AKM
#undef WLAN802_11_MODE
#undef AP_INFO_HS20_INDICATION
#undef HT_OPERATION
#undef VHT_OPERATION
#undef INFO_ASSOC_FAIL_REASON
#undef REMOTE_MAX_PHY_RATE
#undef REMOTE_TX_PACKETS
#undef REMOTE_TX_BYTES
#undef REMOTE_RX_PACKETS
#undef REMOTE_RX_BYTES
#undef REMOTE_LAST_TX_RATE
#undef REMOTE_LAST_RX_RATE
#undef REMOTE_WMM
#undef REMOTE_SUPPORTED_MODE
#undef REMOTE_AMPDU
#undef REMOTE_TX_STBC
#undef REMOTE_RX_STBC
#undef REMOTE_CH_WIDTH
#undef REMOTE_SGI_ENABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#undef REMOTE_PAD
#endif
static const struct
nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {.type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE},
};
/**
* __wlan_hdd_cfg80211_fast_roaming() - enable/disable roaming
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to enable/disable roaming using vendor commands
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_fast_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
uint32_t is_fast_roam_enabled;
eHalStatus status;
int ret;
hdd_station_ctx_t *hddstactx;
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
qca_wlan_vendor_attr);
if (ret) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch Enable flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) {
hddLog(LOGE, FL("attr enable failed"));
return -EINVAL;
}
is_fast_roam_enabled = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]);
hddLog(LOG1, FL("isFastRoamEnabled %d"), is_fast_roam_enabled);
/*
* If framework sends pause_roam, host to send WAIT indication to
* framework if roaming is in progress. This can help framework to
* defer out-network roaming. EBUSY is used to convey wait indication.
*/
if (!is_fast_roam_enabled) {
if (sme_staInMiddleOfRoaming(hdd_ctx->hHal,
adapter->sessionId)) {
hddLog(LOG1, FL("Roaming in progress, do not allow disable"));
return -EBUSY;
}
hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (hddstactx->hdd_ReassocScenario) {
hddLog(LOG1,
FL("Roaming in progress, so unable to disable roaming"));
return -EBUSY;
}
}
/* Update roaming */
status = sme_config_fast_roaming(hdd_ctx->hHal, adapter->sessionId,
is_fast_roam_enabled);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_config_fast_roaming (err=%d)"), status);
return -EINVAL;
}
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_fast_roaming() - enable/disable roaming
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_fast_roaming()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_fast_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_fast_roaming(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct nla_policy
txpower_scale_policy[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE] = { .type = NLA_U8 },
};
/**
* __wlan_hdd_cfg80211_txpower_scale () - txpower scaling
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter;
int ret;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX + 1];
uint8_t scale_value;
VOS_STATUS status;
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
adapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX,
data, data_len, txpower_scale_policy)) {
hddLog(LOGE, "Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]) {
hddLog(LOGE, "attr tx power scale failed");
return -EINVAL;
}
scale_value = nla_get_u8(tb
[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE]);
if (scale_value > MAX_TXPOWER_SCALE) {
hddLog(LOGE, "Invalid tx power scale level");
return -EINVAL;
}
status = wma_set_tx_power_scale(adapter->sessionId, scale_value);
if (VOS_STATUS_SUCCESS != status) {
hddLog(LOGE, "Set tx power scale failed");
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_txpower_scale () - txpower scaling
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_txpower_scale(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_txpower_scale(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct nla_policy txpower_scale_decr_db_policy
[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB] = { .type = NLA_U8 },
};
/**
* __wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter;
int ret;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX + 1];
uint8_t scale_value;
VOS_STATUS status;
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
adapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB_MAX,
data, data_len, txpower_scale_decr_db_policy)) {
hddLog(LOGE, "Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]) {
hddLog(LOGE, "attr tx power decrease db value failed");
return -EINVAL;
}
scale_value = nla_get_u8(tb
[QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_DECR_DB]);
status = wma_set_tx_power_scale_decr_db(adapter->sessionId,
scale_value);
if (VOS_STATUS_SUCCESS != status) {
hddLog(LOGE,"Set tx power decrease db failed");
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_txpower_scale_decr_db () - txpower scaling
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_txpower_scale_decr_db(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_post_get_chain_rssi_rsp - send rsp to user space
* @hdd_ctx: Pointer to hdd context
*
* Return: 0 for success, non-zero for failure
*/
static int hdd_post_get_chain_rssi_rsp(hdd_context_t *hdd_ctx)
{
struct sk_buff *skb = NULL;
struct chain_rssi_result *result =
&hdd_ctx->chain_rssi_context.result;
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
(sizeof(result->chain_rssi) + NLA_HDRLEN) +
(sizeof(result->ant_id) + NLA_HDRLEN) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return -ENOMEM;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI,
sizeof(result->chain_rssi),
result->chain_rssi)) {
hddLog(LOGE, FL("put fail"));
goto nla_put_failure;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO,
sizeof(result->ant_id),
result->ant_id)) {
hddLog(LOGE, FL("put fail"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* Return: 0 on success; error number otherwise.
*/
static int __wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct get_chain_rssi_req_params req_msg;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct hdd_chain_rssi_context *context;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
eHalStatus status;
int retval;
unsigned long rc;
const int mac_len = sizeof(req_msg.peer_macaddr);
int msg_len;
ENTER();
retval = wlan_hdd_validate_context(hdd_ctx);
if (0 != retval)
return retval;
/* nla validation doesn't do exact lengths, do the validation later */
retval = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len, NULL);
if (retval) {
hddLog(LOGE, FL("Invalid ATTR"));
return retval;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hddLog(LOGE, FL("attr mac addr failed"));
return -EINVAL;
}
msg_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]);
if (msg_len != mac_len) {
hddLog(LOGE, FL("Invalid mac address length: %d, expected %d"),
msg_len, mac_len);
return -ERANGE;
}
memcpy(&req_msg.peer_macaddr,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]), mac_len);
spin_lock(&hdd_context_lock);
context = &hdd_ctx->chain_rssi_context;
INIT_COMPLETION(context->response_event);
context->ignore_result = false;
spin_unlock(&hdd_context_lock);
status = sme_get_chain_rssi(hdd_ctx->hHal, &req_msg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_get_chain_rssi failed(err=%d)"), status);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_CHAIN_RSSI));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
spin_lock(&hdd_context_lock);
context->ignore_result = true;
spin_unlock(&hdd_context_lock);
return -ETIMEDOUT;
}
retval = hdd_post_get_chain_rssi_rsp(hdd_ctx);
if (retval)
hddLog(LOGE,
FL("Failed to send chain rssi to user space"));
EXIT();
return retval;
}
/**
* wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct
nla_policy
qca_wlan_vendor_peer_flush_pending_policy
[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_PEER_ADDR] = {.type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE},
[QCA_WLAN_VENDOR_ATTR_AC] = { .type = NLA_U8 },
};
/**
* __wlan_hdd_cfg80211_peer_flush_tids() - flush peer pending packets
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to flush peer pending packets using vendor commands
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_peer_flush_pending(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX + 1];
struct sme_flush_pending flush_pending;
eHalStatus status;
int ret;
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX, data,
data_len, qca_wlan_vendor_peer_flush_pending_policy)) {
hddLog(LOGE, FL("Invalid attribute"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR]) {
hddLog(LOGE,
FL("Attribute peerMac not provided"));
return -EINVAL;
}
memcpy(flush_pending.peer_addr.bytes,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_PEER_ADDR]),
VOS_MAC_ADDR_SIZE);
if (!tb[QCA_WLAN_VENDOR_ATTR_AC]) {
hddLog(LOGE, FL("Attribute AC not provided"));
return -EINVAL;
}
flush_pending.flush_ac = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_AC]);
hddLog(LOG1, FL("flush ac = %02x"), flush_pending.flush_ac & 0x0f);
flush_pending.session_id = adapter->sessionId;
hddLog(LOG1, FL("session_id = %d"), flush_pending.session_id);
status = sme_peer_flush_pending(hdd_ctx->hHal, &flush_pending);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("sme_config_peer_flush_pending (err=%d)"),
status);
return -EINVAL;
}
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_peer_flush_tids() - flush peer pending packets
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_peer_flush_ac()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_peer_flush_pending(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_peer_flush_pending(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
{
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = is_driver_dfs_capable
},
#ifdef WLAN_FEATURE_NAN
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_nan_request
},
#endif
#ifdef WLAN_FEATURE_STATS_EXT
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STATS_EXT,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_stats_ext_request
},
#endif
#ifdef FEATURE_WLAN_EXTSCAN
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_start
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_stop
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_extscan_get_valid_channels
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_extscan_get_capabilities
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_get_cached_results
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_set_bssid_hotlist
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_reset_bssid_hotlist
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_set_significant_change
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_reset_significant_change
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_LIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_epno_list
},
#endif /* FEATURE_WLAN_EXTSCAN */
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ll_stats_clear
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ll_stats_set
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ll_stats_get
},
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
#ifdef FEATURE_WLAN_TDLS
/* EXT TDLS */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_exttdls_enable
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_exttdls_disable
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_exttdls_get_status
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_supported_features
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_set_scanning_mac_oui
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_disable_dfs_chan_scan
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_concurrency_matrix
},
#ifdef WLAN_FEATURE_APFIND
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_APFIND,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_apfind_cmd
},
#endif /* WLAN_FEATURE_APFIND */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_do_acs
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_features
},
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_keymgmt_set_key
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_wifi_configuration_set
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_wifi_configuration_get
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_set_ext_roam_params
},
#ifdef FEATURE_WLAN_EXTSCAN
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_SET_PASSPOINT_LIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_passpoint_list
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_RESET_PASSPOINT_LIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_reset_passpoint_list
},
#endif /* FEATURE_WLAN_EXTSCAN */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_wifi_info
},
/* OCB commands */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ocb_set_config
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ocb_set_utc_time
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ocb_start_timing_advert
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ocb_stop_timing_advert
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ocb_get_tsf_timer
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_dcc_get_stats
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_dcc_clear_stats
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_dcc_update_ndl
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_logger_supp_feature
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_wifi_logger_start
},
#ifdef FEATURE_WLAN_TDLS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_tdls_capabilities
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_link_properties
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_wifi_logger_get_ring_data
},
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_offloaded_packets
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_monitor_rssi
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_ns_offload
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SETBAND,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_setband
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_bpf_offload
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_bus_size
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_wakelock_stats
},
#ifdef WLAN_FEATURE_NAN_DATAPATH
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_process_ndp_cmd
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_acs_dfs_mode
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_STA_CONNECT_ROAM_POLICY,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_sta_roam_policy
},
#ifdef FEATURE_WLAN_CH_AVOID
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_avoid_freq
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_sap_configuration_set
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_STATION,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = hdd_cfg80211_get_station_cmd
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAMING,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_fast_roaming
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_txpower_scale
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE_DECR_DB,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_txpower_scale_decr_db
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_chain_rssi
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_ll_stats_ext_set_param
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_peer_flush_pending
},
};
/*
* FUNCTION: wlan_hdd_cfg80211_wiphy_alloc
* This function is called by hdd_wlan_startup()
* during initialization.
* This function is used to allocate wiphy structure.
*/
struct wiphy *wlan_hdd_cfg80211_wiphy_alloc(int priv_size)
{
struct wiphy *wiphy;
ENTER();
/*
* Create wiphy device
*/
wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, priv_size);
if (!wiphy)
{
/* Print error and jump into err label and free the memory */
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wiphy init failed", __func__);
return NULL;
}
return wiphy;
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,4,0)) || \
defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT)
/**
* hdd_config_sched_scan_plans_to_wiphy() - configure sched scan plans to wiphy
* @wiphy: pointer to wiphy
* @config: pointer to config
*
* Return: None
*/
static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
hdd_config_t *config)
{
wiphy->max_sched_scan_plans = MAX_SCHED_SCAN_PLANS;
if (config->max_sched_scan_plan_interval)
wiphy->max_sched_scan_plan_interval =
config->max_sched_scan_plan_interval;
if (config->max_sched_scan_plan_iterations)
wiphy->max_sched_scan_plan_iterations =
config->max_sched_scan_plan_iterations;
}
#else
static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
hdd_config_t *config)
{
}
#endif
#ifdef CFG80211_SCAN_RANDOM_MAC_ADDR
static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
{
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
}
#else
static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
{
return;
}
#endif
/**
* wlan_hdd_cfg80211_add_connected_pno_support() - Set connected PNO support
* @wiphy: Pointer to wireless phy
*
* This function is used to set connected PNO support to kernel
*
* Return: None
*/
#if defined(CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN)
static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI);
}
#else
static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy)
{
return;
}
#endif
#ifdef CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME
/**
* wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA frms
* @wiphy: Pointer to wiphy
*
* This function is used to indicate the support of source mac address
* randomization of management action frames
*
* Return: None
*/
static void
wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
}
#else
static void
wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
{
return;
}
#endif
#if defined(WLAN_FEATURE_FILS_SK) && defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT)
static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_SK_OFFLOAD);
}
#else
static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy)
{
}
#endif
/*
* FUNCTION: wlan_hdd_cfg80211_init
* This function is called by hdd_wlan_startup()
* during initialization.
* This function is used to initialize and register wiphy structure.
*/
int wlan_hdd_cfg80211_init(struct device *dev,
struct wiphy *wiphy,
hdd_config_t *pCfg
)
{
int i, j;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
ENTER();
/* Now bind the underlying wlan device with wiphy */
set_wiphy_dev(wiphy, dev);
wiphy->mgmt_stypes = wlan_hdd_txrx_stypes;
/* This will disable updating of NL channels from passive to
* active if a beacon is received on passive channel. */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
#else
wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS)
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME
| WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD
| WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL
#ifdef FEATURE_WLAN_STA_4ADDR_SCHEME
| WIPHY_FLAG_4ADDR_STATION
#endif
| WIPHY_FLAG_OFFCHAN_TX;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
#else
wiphy->country_ie_pref = NL80211_COUNTRY_IE_IGNORE_CORE;
#endif
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS)
wiphy->wowlan = &wowlan_support_cfg80211_init;
#else
wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
wiphy->wowlan.n_patterns = WOWL_MAX_PTRNS_ALLOWED;
wiphy->wowlan.pattern_min_len = 1;
wiphy->wowlan.pattern_max_len = WOWL_PTRN_MAX_SIZE;
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
if (pCfg->isFastTransitionEnabled
#ifdef FEATURE_WLAN_LFR
|| pCfg->isFastRoamIniFeatureEnabled
#endif
#ifdef FEATURE_WLAN_ESE
|| pCfg->isEseIniFeatureEnabled
#endif
)
{
wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
}
#endif
#ifdef FEATURE_WLAN_TDLS
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS
| WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
wiphy->features |= NL80211_FEATURE_HT_IBSS;
#ifdef FEATURE_WLAN_SCAN_PNO
if (pCfg->configPNOScanSupport)
{
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
wiphy->max_sched_scan_ssids = SIR_PNO_MAX_SUPP_NETWORKS;
wiphy->max_match_sets = SIR_PNO_MAX_SUPP_NETWORKS;
wiphy->max_sched_scan_ie_len = SIR_MAC_MAX_IE_LENGTH;
}
wlan_hdd_cfg80211_add_connected_pno_support(wiphy);
#endif/*FEATURE_WLAN_SCAN_PNO*/
#if defined QCA_WIFI_FTM
if (vos_get_conparam() != VOS_FTM_MODE) {
#endif
/* even with WIPHY_FLAG_CUSTOM_REGULATORY,
driver can still register regulatory callback and
it will get regulatory settings in wiphy->band[], but
driver need to determine what to do with both
regulatory settings */
wiphy->reg_notifier = wlan_hdd_linux_reg_notifier;
#if defined QCA_WIFI_FTM
}
#endif
wiphy->max_scan_ssids = MAX_SCAN_SSID;
wiphy->max_scan_ie_len = SIR_MAC_MAX_ADD_IE_LENGTH;
wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS;
/* Supports STATION & AD-HOC modes right now */
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_ADHOC)
| BIT(NL80211_IFTYPE_P2P_CLIENT)
| BIT(NL80211_IFTYPE_P2P_GO)
| BIT(NL80211_IFTYPE_AP)
| BIT(NL80211_IFTYPE_MONITOR);
if( pCfg->advertiseConcurrentOperation )
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) || defined(WITH_BACKPORTS)
if( pCfg->enableMCC ) {
int i;
for (i = 0; i < ARRAY_SIZE(wlan_hdd_iface_combination); i++) {
if( !pCfg->allowMCCGODiffBI )
wlan_hdd_iface_combination[i].beacon_int_infra_match = true;
}
}
wiphy->n_iface_combinations = ARRAY_SIZE(wlan_hdd_iface_combination);
wiphy->iface_combinations = wlan_hdd_iface_combination;
#endif
}
/* Before registering we need to update the HT capability based
* on ini values */
if( !pCfg->ShortGI20MhzEnable )
{
wlan_hdd_band_2_4_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20;
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20;
}
if( !pCfg->ShortGI40MhzEnable )
{
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
}
if( !pCfg->nChannelBondingMode5GHz )
{
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
}
/*
* In case of static linked driver at the time of driver unload,
* module exit doesn't happens. Module cleanup helps in cleaning
* of static memory.
* If driver load happens statically, at the time of driver unload,
* wiphy flags don't get reset because of static memory.
* It's better not to store channel in static memory.
*/
wiphy->bands[IEEE80211_BAND_2GHZ] = &wlan_hdd_band_2_4_GHZ;
wiphy->bands[IEEE80211_BAND_2GHZ]->channels =
vos_mem_malloc(sizeof(hdd_channels_2_4_GHZ));
if (wiphy->bands[IEEE80211_BAND_2GHZ]->channels == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not enough memory to allocate channels"));
return -ENOMEM;
}
vos_mem_copy(wiphy->bands[IEEE80211_BAND_2GHZ]->channels,
&hdd_channels_2_4_GHZ[0],
sizeof(hdd_channels_2_4_GHZ));
if (hdd_is_5g_supported(pHddCtx) &&
((eHDD_DOT11_MODE_11b != pCfg->dot11Mode) &&
(eHDD_DOT11_MODE_11g != pCfg->dot11Mode) &&
(eHDD_DOT11_MODE_11b_ONLY != pCfg->dot11Mode) &&
(eHDD_DOT11_MODE_11g_ONLY != pCfg->dot11Mode)))
{
wiphy->bands[IEEE80211_BAND_5GHZ] = &wlan_hdd_band_5_GHZ;
wiphy->bands[IEEE80211_BAND_5GHZ]->channels =
vos_mem_malloc(sizeof(hdd_channels_5_GHZ));
if (wiphy->bands[IEEE80211_BAND_5GHZ]->channels == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not enough memory to allocate channels"));
vos_mem_free(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
wiphy->bands[IEEE80211_BAND_2GHZ]->channels = NULL;
return -ENOMEM;
}
vos_mem_copy(wiphy->bands[IEEE80211_BAND_5GHZ]->channels,
&hdd_channels_5_GHZ[0],
sizeof(hdd_channels_5_GHZ));
}
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
{
if (NULL == wiphy->bands[i])
continue;
for (j = 0; j < wiphy->bands[i]->n_channels; j++)
{
struct ieee80211_supported_band *band = wiphy->bands[i];
if (IEEE80211_BAND_2GHZ == i && eCSR_BAND_5G == pCfg->nBandCapability) // 5G only
{
#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY
// Enable social channels for P2P
if (WLAN_HDD_IS_SOCIAL_CHANNEL(band->channels[j].center_freq))
band->channels[j].flags &= ~IEEE80211_CHAN_DISABLED;
else
#endif
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
else if (IEEE80211_BAND_5GHZ == i && eCSR_BAND_24 == pCfg->nBandCapability) // 2G only
{
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
}
}
/*Initialise the supported cipher suite details*/
wiphy->cipher_suites = hdd_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(hdd_cipher_suites);
/*signal strength in mBm (100*dBm) */
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION;
wiphy->n_vendor_commands = ARRAY_SIZE(hdd_wiphy_vendor_commands);
wiphy->vendor_commands = hdd_wiphy_vendor_commands;
wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events;
wiphy->n_vendor_events = ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,4,0)) || \
defined (DFS_MASTER_OFFLOAD_IND_SUPPORT) || defined(WITH_BACKPORTS)
if (pCfg->enableDFSMasterCap) {
wiphy->flags |= WIPHY_FLAG_DFS_OFFLOAD;
}
#endif
wiphy->max_ap_assoc_sta = pHddCtx->max_peers;
#ifdef QCA_HT_2040_COEX
wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
#endif
#ifdef CHANNEL_SWITCH_SUPPORTED
wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
#endif
if (pCfg->sub_20_channel_width)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)) || \
defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT);
#endif
wlan_hdd_cfg80211_set_wiphy_fils_feature(wiphy);
hdd_config_sched_scan_plans_to_wiphy(wiphy, pCfg);
wlan_hdd_cfg80211_scan_randomization_init(wiphy);
wlan_hdd_cfg80211_action_frame_randomization_init(wiphy);
EXIT();
return 0;
}
/**
* wlan_hdd_cfg80211_deinit - Deinit cfg80211
* @ wiphy: the wiphy to validate against
*
* this function deinit cfg80211 and cleanup the
* memory allocated in wlan_hdd_cfg80211_init
*
* Return: void
*/
void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy)
{
int i;
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
if (NULL != wiphy->bands[i] &&
(NULL != wiphy->bands[i]->channels)) {
vos_mem_free(wiphy->bands[i]->channels);
wiphy->bands[i]->channels = NULL;
}
}
vos_reset_global_reg_params();
}
/*
* In this function, wiphy structure is updated after VOSS
* initialization. In wlan_hdd_cfg80211_init, only the
* default values will be initialized. The final initialization
* of all required members can be done here.
*/
void wlan_hdd_update_wiphy(struct wiphy *wiphy,
hdd_context_t *ctx)
{
uint32_t val32;
uint16_t val16;
tSirMacHTCapabilityInfo *ht_cap_info;
eHalStatus status;
wiphy->max_ap_assoc_sta = ctx->max_peers;
if (!sme_IsFeatureSupportedByFW(DOT11AC)) {
wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap.vht_supported = 0;
wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap.cap = 0;
wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.vht_supported = 0;
wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap = 0;
}
status = ccmCfgGetInt(ctx->hHal, WNI_CFG_HT_CAP_INFO, &val32);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: could not get HT capability info",
__func__);
val32 = 0;
}
val16 = (uint16_t)val32;
ht_cap_info = (tSirMacHTCapabilityInfo *)&val16;
if (ht_cap_info->txSTBC == TRUE) {
if (NULL != wiphy->bands[IEEE80211_BAND_2GHZ])
wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap |=
IEEE80211_HT_CAP_TX_STBC;
if (NULL != wiphy->bands[IEEE80211_BAND_5GHZ])
wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap.cap |=
IEEE80211_HT_CAP_TX_STBC;
}
}
/* In this function we are registering wiphy. */
int wlan_hdd_cfg80211_register(struct wiphy *wiphy)
{
ENTER();
/* Register our wiphy dev with cfg80211 */
if (0 > wiphy_register(wiphy))
{
/* print error */
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__);
return -EIO;
}
EXIT();
return 0;
}
/*
HDD function to update wiphy capability based on target offload status.
wlan_hdd_cfg80211_init() does initialization of all wiphy related
capability even before downloading firmware to the target. In discrete
case, host will get know certain offload capability (say sched_scan
caps) only after downloading firmware to the target and target boots up.
This function is used to override setting done in wlan_hdd_cfg80211_init()
based on target capability.
*/
void wlan_hdd_cfg80211_update_wiphy_caps(struct wiphy *wiphy)
{
#ifdef FEATURE_WLAN_SCAN_PNO
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hdd_config_t *pCfg = pHddCtx->cfg_ini;
/* wlan_hdd_cfg80211_init() sets sched_scan caps already in wiphy before
* control comes here. Here just we need to clear it if firmware doesn't
* have PNO support. */
if (!pCfg->PnoOffload) {
wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
wiphy->max_sched_scan_ssids = 0;