blob: 3c536884099b3d881afde2c2176d97863d94baed [file] [log] [blame]
/*
* Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wlan_hdd_cfg80211.c
*
* WLAN Host Device Driver cfg80211 APIs implementation
*
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include "osif_sync.h"
#include <wlan_hdd_includes.h>
#include <net/arp.h>
#include <net/cfg80211.h>
#include <wlan_hdd_wowl.h>
#include <ani_global.h>
#include "sir_params.h"
#include "dot11f.h"
#include "wlan_hdd_assoc.h"
#include "wlan_hdd_wext.h"
#include "sme_api.h"
#include "sme_power_save_api.h"
#include "wlan_hdd_p2p.h"
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_hostapd.h"
#include "wlan_hdd_softap_tx_rx.h"
#include "wlan_hdd_main.h"
#include "wlan_hdd_power.h"
#include "wlan_hdd_trace.h"
#include "qdf_str.h"
#include "qdf_trace.h"
#include "qdf_types.h"
#include "cds_utils.h"
#include "cds_sched.h"
#include "wlan_hdd_scan.h"
#include <qc_sap_ioctl.h>
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_wmm.h"
#include "wma_types.h"
#include "wma.h"
#include "wma_twt.h"
#include "wlan_hdd_misc.h"
#include "wlan_hdd_nan.h"
#include "wlan_logging_sock_svc.h"
#include "sap_api.h"
#include "csr_api.h"
#include "pld_common.h"
#include "wmi_unified_param.h"
#ifdef WLAN_UMAC_CONVERGENCE
#include "wlan_cfg80211.h"
#endif
#include <cdp_txrx_handle.h>
#include <wlan_cfg80211_scan.h>
#include <wlan_cfg80211_ftm.h>
#include "wlan_hdd_ext_scan.h"
#include "wlan_hdd_stats.h"
#include "cds_api.h"
#include "wlan_policy_mgr_api.h"
#include "qwlan_version.h"
#include "wlan_hdd_ocb.h"
#include "wlan_hdd_tsf.h"
#include "wlan_hdd_subnet_detect.h"
#include <wlan_hdd_regulatory.h>
#include "wlan_hdd_lpass.h"
#include "wlan_hdd_nan_datapath.h"
#include "wlan_hdd_disa.h"
#include "wlan_osif_request_manager.h"
#include "wlan_hdd_he.h"
#ifdef FEATURE_WLAN_APF
#include "wlan_hdd_apf.h"
#endif
#include "wlan_hdd_fw_state.h"
#include "wlan_hdd_mpta_helper.h"
#include <cdp_txrx_cmn.h>
#include <cdp_txrx_misc.h>
#include <qca_vendor.h>
#include "wlan_pmo_ucfg_api.h"
#include "os_if_wifi_pos.h"
#include "wlan_utility.h"
#include "wlan_reg_ucfg_api.h"
#include "wifi_pos_api.h"
#include "wlan_hdd_spectralscan.h"
#include "wlan_ipa_ucfg_api.h"
#include <wlan_cfg80211_mc_cp_stats.h>
#include <wlan_cp_stats_mc_ucfg_api.h>
#include "wlan_tdls_cfg_api.h"
#include <wlan_hdd_bss_transition.h>
#include <wlan_hdd_concurrency_matrix.h>
#include <wlan_hdd_p2p_listen_offload.h>
#include <wlan_hdd_rssi_monitor.h>
#include <wlan_hdd_sap_cond_chan_switch.h>
#include <wlan_hdd_station_info.h>
#include <wlan_hdd_tx_power.h>
#include <wlan_hdd_active_tos.h>
#include <wlan_hdd_sar_limits.h>
#include <wlan_hdd_ota_test.h>
#include "wlan_policy_mgr_ucfg.h"
#include "wlan_mlme_ucfg_api.h"
#include "wlan_mlme_public_struct.h"
#include "wlan_extscan_ucfg_api.h"
#include "wlan_mlme_ucfg_api.h"
#include "wlan_pmo_cfg.h"
#include "cfg_ucfg_api.h"
#include "wlan_crypto_global_api.h"
#include "wlan_nl_to_crypto_params.h"
#include "wlan_crypto_global_def.h"
#include "cdp_txrx_cfg.h"
#include "wlan_hdd_object_manager.h"
#include "nan_ucfg_api.h"
#include "wlan_fwol_ucfg_api.h"
#include "wlan_cfg80211_crypto.h"
#include "wlan_scan_ucfg_api.h"
#include "wlan_hdd_coex_config.h"
#include "wlan_hdd_bcn_recv.h"
#define g_mode_rates_size (12)
#define a_mode_rates_size (8)
/*
* Android CTS verifier needs atleast this much wait time (in msec)
*/
#define MAX_REMAIN_ON_CHANNEL_DURATION (5000)
/*
* Refer @tCfgProtection structure for definition of the bit map.
* below value is obtained by setting the following bit-fields.
* enable obss, fromllb, overlapOBSS and overlapFromllb protection.
*/
#define IBSS_CFG_PROTECTION_ENABLE_MASK 0x8282
#define HDD2GHZCHAN(freq, chan, flag) { \
.band = HDD_NL80211_BAND_2GHZ, \
.center_freq = (freq), \
.hw_value = (chan), \
.flags = (flag), \
.max_antenna_gain = 0, \
.max_power = 0, \
}
#define HDD5GHZCHAN(freq, chan, flag) { \
.band = HDD_NL80211_BAND_5GHZ, \
.center_freq = (freq), \
.hw_value = (chan), \
.flags = (flag), \
.max_antenna_gain = 0, \
.max_power = 0, \
}
#define HDD_G_MODE_RATETAB(rate, rate_id, flag) \
{ \
.bitrate = rate, \
.hw_value = rate_id, \
.flags = flag, \
}
#define IS_DFS_MODE_VALID(mode) ((mode >= DFS_MODE_NONE && \
mode <= DFS_MODE_DEPRIORITIZE))
/*
* Number of DPTRACE records to dump when a cfg80211 disconnect with reason
* WLAN_REASON_DEAUTH_LEAVING DEAUTH is received from user-space.
*/
#define WLAN_DEAUTH_DPTRACE_DUMP_COUNT 100
#ifndef WLAN_CIPHER_SUITE_GCMP
#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08
#endif
#ifndef WLAN_CIPHER_SUITE_GCMP_256
#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09
#endif
static const u32 hdd_gcmp_cipher_suits[] = {
WLAN_CIPHER_SUITE_GCMP,
WLAN_CIPHER_SUITE_GCMP_256,
};
static const u32 hdd_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
#ifdef FEATURE_WLAN_ESE
#define WLAN_CIPHER_SUITE_BTK 0x004096fe /* use for BTK */
#define WLAN_CIPHER_SUITE_KRK 0x004096ff /* use for KRK */
WLAN_CIPHER_SUITE_BTK,
WLAN_CIPHER_SUITE_KRK,
WLAN_CIPHER_SUITE_CCMP,
#else
WLAN_CIPHER_SUITE_CCMP,
#endif
#ifdef FEATURE_WLAN_WAPI
WLAN_CIPHER_SUITE_SMS4,
#endif
#ifdef WLAN_FEATURE_11W
WLAN_CIPHER_SUITE_AES_CMAC,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
WLAN_CIPHER_SUITE_BIP_GMAC_128,
WLAN_CIPHER_SUITE_BIP_GMAC_256,
#endif
#endif
};
static const struct ieee80211_channel hdd_channels_2_4_ghz[] = {
HDD2GHZCHAN(2412, 1, 0),
HDD2GHZCHAN(2417, 2, 0),
HDD2GHZCHAN(2422, 3, 0),
HDD2GHZCHAN(2427, 4, 0),
HDD2GHZCHAN(2432, 5, 0),
HDD2GHZCHAN(2437, 6, 0),
HDD2GHZCHAN(2442, 7, 0),
HDD2GHZCHAN(2447, 8, 0),
HDD2GHZCHAN(2452, 9, 0),
HDD2GHZCHAN(2457, 10, 0),
HDD2GHZCHAN(2462, 11, 0),
HDD2GHZCHAN(2467, 12, 0),
HDD2GHZCHAN(2472, 13, 0),
HDD2GHZCHAN(2484, 14, 0),
};
static const struct ieee80211_channel hdd_channels_5_ghz[] = {
HDD5GHZCHAN(5180, 36, 0),
HDD5GHZCHAN(5200, 40, 0),
HDD5GHZCHAN(5220, 44, 0),
HDD5GHZCHAN(5240, 48, 0),
HDD5GHZCHAN(5260, 52, 0),
HDD5GHZCHAN(5280, 56, 0),
HDD5GHZCHAN(5300, 60, 0),
HDD5GHZCHAN(5320, 64, 0),
HDD5GHZCHAN(5500, 100, 0),
HDD5GHZCHAN(5520, 104, 0),
HDD5GHZCHAN(5540, 108, 0),
HDD5GHZCHAN(5560, 112, 0),
HDD5GHZCHAN(5580, 116, 0),
HDD5GHZCHAN(5600, 120, 0),
HDD5GHZCHAN(5620, 124, 0),
HDD5GHZCHAN(5640, 128, 0),
HDD5GHZCHAN(5660, 132, 0),
HDD5GHZCHAN(5680, 136, 0),
HDD5GHZCHAN(5700, 140, 0),
HDD5GHZCHAN(5720, 144, 0),
HDD5GHZCHAN(5745, 149, 0),
HDD5GHZCHAN(5765, 153, 0),
HDD5GHZCHAN(5785, 157, 0),
HDD5GHZCHAN(5805, 161, 0),
HDD5GHZCHAN(5825, 165, 0),
};
#ifdef WLAN_FEATURE_DSRC
static const struct ieee80211_channel hdd_channels_dot11p[] = {
HDD5GHZCHAN(5852, 170, 0),
HDD5GHZCHAN(5855, 171, 0),
HDD5GHZCHAN(5860, 172, 0),
HDD5GHZCHAN(5865, 173, 0),
HDD5GHZCHAN(5870, 174, 0),
HDD5GHZCHAN(5875, 175, 0),
HDD5GHZCHAN(5880, 176, 0),
HDD5GHZCHAN(5885, 177, 0),
HDD5GHZCHAN(5890, 178, 0),
HDD5GHZCHAN(5895, 179, 0),
HDD5GHZCHAN(5900, 180, 0),
HDD5GHZCHAN(5905, 181, 0),
HDD5GHZCHAN(5910, 182, 0),
HDD5GHZCHAN(5915, 183, 0),
HDD5GHZCHAN(5920, 184, 0),
};
#else
static const struct ieee80211_channel hdd_etsi13_srd_ch[] = {
HDD5GHZCHAN(5845, 169, 0),
HDD5GHZCHAN(5865, 173, 0),
};
#endif
#define band_2_ghz_channels_size sizeof(hdd_channels_2_4_ghz)
#ifdef WLAN_FEATURE_DSRC
#define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \
sizeof(hdd_channels_dot11p))
#else
#define band_5_ghz_chanenls_size (sizeof(hdd_channels_5_ghz) + \
sizeof(hdd_etsi13_srd_ch))
#endif
static struct ieee80211_rate g_mode_rates[] = {
HDD_G_MODE_RATETAB(10, 0x1, 0),
HDD_G_MODE_RATETAB(20, 0x2, 0),
HDD_G_MODE_RATETAB(55, 0x4, 0),
HDD_G_MODE_RATETAB(110, 0x8, 0),
HDD_G_MODE_RATETAB(60, 0x10, 0),
HDD_G_MODE_RATETAB(90, 0x20, 0),
HDD_G_MODE_RATETAB(120, 0x40, 0),
HDD_G_MODE_RATETAB(180, 0x80, 0),
HDD_G_MODE_RATETAB(240, 0x100, 0),
HDD_G_MODE_RATETAB(360, 0x200, 0),
HDD_G_MODE_RATETAB(480, 0x400, 0),
HDD_G_MODE_RATETAB(540, 0x800, 0),
};
static struct ieee80211_rate a_mode_rates[] = {
HDD_G_MODE_RATETAB(60, 0x10, 0),
HDD_G_MODE_RATETAB(90, 0x20, 0),
HDD_G_MODE_RATETAB(120, 0x40, 0),
HDD_G_MODE_RATETAB(180, 0x80, 0),
HDD_G_MODE_RATETAB(240, 0x100, 0),
HDD_G_MODE_RATETAB(360, 0x200, 0),
HDD_G_MODE_RATETAB(480, 0x400, 0),
HDD_G_MODE_RATETAB(540, 0x800, 0),
};
static struct ieee80211_supported_band wlan_hdd_band_2_4_ghz = {
.channels = NULL,
.n_channels = ARRAY_SIZE(hdd_channels_2_4_ghz),
.band = HDD_NL80211_BAND_2GHZ,
.bitrates = g_mode_rates,
.n_bitrates = g_mode_rates_size,
.ht_cap.ht_supported = 1,
.ht_cap.cap = IEEE80211_HT_CAP_SGI_20
| IEEE80211_HT_CAP_GRN_FLD
| IEEE80211_HT_CAP_DSSSCCK40
| IEEE80211_HT_CAP_LSIG_TXOP_PROT
| IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40,
.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
.ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
.ht_cap.mcs.rx_highest = cpu_to_le16(72),
.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
};
static struct ieee80211_supported_band wlan_hdd_band_5_ghz = {
.channels = NULL,
.n_channels = ARRAY_SIZE(hdd_channels_5_ghz),
.band = HDD_NL80211_BAND_5GHZ,
.bitrates = a_mode_rates,
.n_bitrates = a_mode_rates_size,
.ht_cap.ht_supported = 1,
.ht_cap.cap = IEEE80211_HT_CAP_SGI_20
| IEEE80211_HT_CAP_GRN_FLD
| IEEE80211_HT_CAP_DSSSCCK40
| IEEE80211_HT_CAP_LSIG_TXOP_PROT
| IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40,
.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
.ht_cap.mcs.rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
.ht_cap.mcs.rx_highest = cpu_to_le16(72),
.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
.vht_cap.vht_supported = 1,
};
/* This structure contain information what kind of frame are expected in
* TX/RX direction for each kind of interface
*/
static const struct ieee80211_txrx_stypes
wlan_hdd_txrx_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ACTION) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_AUTH),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
[NL80211_IFTYPE_ADHOC] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ACTION) |
BIT(SIR_MAC_MGMT_PROBE_REQ),
},
[NL80211_IFTYPE_P2P_GO] = {
/* This is also same as for SoftAP */
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
};
/* Interface limits and combinations registered by the driver */
/* STA ( + STA ) combination */
static const struct ieee80211_iface_limit
wlan_hdd_sta_iface_limit[] = {
{
.max = 3, /* p2p0 is a STA as well */
.types = BIT(NL80211_IFTYPE_STATION),
},
};
/* 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 */
static const struct ieee80211_iface_limit
wlan_hdd_ap_iface_limit[] = {
{
.max = (QDF_MAX_NO_OF_SAP_MODE + SAP_MAX_OBSS_STA_CNT),
.types = BIT(NL80211_IFTYPE_AP),
},
};
/* P2P limit */
static const struct ieee80211_iface_limit
wlan_hdd_p2p_iface_limit[] = {
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_GO),
},
};
static const struct ieee80211_iface_limit
wlan_hdd_sta_ap_iface_limit[] = {
{
/* We need 1 extra STA interface for OBSS scan when SAP starts
* with HT40 in STA+SAP concurrency mode
*/
.max = (1 + SAP_MAX_OBSS_STA_CNT),
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = QDF_MAX_NO_OF_SAP_MODE,
.types = BIT(NL80211_IFTYPE_AP),
},
};
/* STA + P2P combination */
static const struct ieee80211_iface_limit
wlan_hdd_sta_p2p_iface_limit[] = {
{
/* One reserved for dedicated P2PDEV usage */
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION)
},
{
/* Support for two identical (GO + GO or CLI + CLI)
* or dissimilar (GO + CLI) P2P interfaces
*/
.max = 2,
.types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT),
},
};
/* STA + AP + P2PGO combination */
static const struct ieee80211_iface_limit
wlan_hdd_sta_ap_p2pgo_iface_limit[] = {
/* Support for AP+P2PGO interfaces */
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_GO)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP)
}
};
/* SAP + P2P combination */
static const struct ieee80211_iface_limit
wlan_hdd_sap_p2p_iface_limit[] = {
{
/* 1 dedicated for p2p0 which is a STA type */
.max = 1,
.types = BIT(NL80211_IFTYPE_STATION)
},
{
/* The p2p interface in SAP+P2P can be GO/CLI.
* The p2p connection can be formed on p2p0 or p2p-p2p0-x.
*/
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT)
},
{
/* SAP+GO to support only one SAP interface */
.max = 1,
.types = BIT(NL80211_IFTYPE_AP)
}
};
/* P2P + P2P combination */
static const struct ieee80211_iface_limit
wlan_hdd_p2p_p2p_iface_limit[] = {
{
/* 1 dedicated for p2p0 which is a STA type */
.max = 1,
.types = BIT(NL80211_IFTYPE_STATION)
},
{
/* The p2p interface in P2P+P2P can be GO/CLI.
* For P2P+P2P, the new interfaces are formed on p2p-p2p0-x.
*/
.max = 2,
.types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT)
},
};
static const struct ieee80211_iface_limit
wlan_hdd_mon_iface_limit[] = {
{
.max = 3, /* Monitor interface */
.types = BIT(NL80211_IFTYPE_MONITOR),
},
};
static struct ieee80211_iface_combination
wlan_hdd_iface_combination[] = {
/* STA */
{
.limits = wlan_hdd_sta_iface_limit,
.num_different_channels = 2,
.max_interfaces = 3,
.n_limits = ARRAY_SIZE(wlan_hdd_sta_iface_limit),
},
/* ADHOC */
{
.limits = wlan_hdd_adhoc_iface_limit,
.num_different_channels = 2,
.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 + QDF_MAX_NO_OF_SAP_MODE),
.n_limits = ARRAY_SIZE(wlan_hdd_ap_iface_limit),
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \
defined(CFG80211_BEACON_INTERVAL_BACKPORT)
.beacon_int_min_gcd = 1,
#endif
},
/* P2P */
{
.limits = wlan_hdd_p2p_iface_limit,
.num_different_channels = 2,
.max_interfaces = 2,
.n_limits = ARRAY_SIZE(wlan_hdd_p2p_iface_limit),
},
/* STA + AP */
{
.limits = wlan_hdd_sta_ap_iface_limit,
.num_different_channels = 2,
.max_interfaces = (1 + SAP_MAX_OBSS_STA_CNT + QDF_MAX_NO_OF_SAP_MODE),
.n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_iface_limit),
.beacon_int_infra_match = true,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) || \
defined(CFG80211_BEACON_INTERVAL_BACKPORT)
.beacon_int_min_gcd = 1,
#endif
},
/* STA + P2P */
{
.limits = wlan_hdd_sta_p2p_iface_limit,
.num_different_channels = 2,
/* one interface reserved for P2PDEV dedicated usage */
.max_interfaces = 4,
.n_limits = ARRAY_SIZE(wlan_hdd_sta_p2p_iface_limit),
.beacon_int_infra_match = true,
},
/* STA + P2P GO + SAP */
{
.limits = wlan_hdd_sta_ap_p2pgo_iface_limit,
/* we can allow 3 channels for three different persona
* but due to firmware limitation, allow max 2 concrnt channels.
*/
.num_different_channels = 2,
/* one interface reserved for P2PDEV dedicated usage */
.max_interfaces = 4,
.n_limits = ARRAY_SIZE(wlan_hdd_sta_ap_p2pgo_iface_limit),
.beacon_int_infra_match = true,
},
/* SAP + P2P */
{
.limits = wlan_hdd_sap_p2p_iface_limit,
.num_different_channels = 2,
/* 1-p2p0 + 1-SAP + 1-P2P (on p2p0 or p2p-p2p0-x) */
.max_interfaces = 3,
.n_limits = ARRAY_SIZE(wlan_hdd_sap_p2p_iface_limit),
.beacon_int_infra_match = true,
},
/* P2P + P2P */
{
.limits = wlan_hdd_p2p_p2p_iface_limit,
.num_different_channels = 2,
/* 1-p2p0 + 2-P2P (on p2p-p2p0-x) */
.max_interfaces = 3,
.n_limits = ARRAY_SIZE(wlan_hdd_p2p_p2p_iface_limit),
.beacon_int_infra_match = true,
},
/* Monitor */
{
.limits = wlan_hdd_mon_iface_limit,
.max_interfaces = 3,
.num_different_channels = 2,
.n_limits = ARRAY_SIZE(wlan_hdd_mon_iface_limit),
},
};
static struct cfg80211_ops wlan_hdd_cfg80211_ops;
#ifdef WLAN_NL80211_TESTMODE
enum wlan_hdd_tm_attr {
WLAN_HDD_TM_ATTR_INVALID = 0,
WLAN_HDD_TM_ATTR_CMD = 1,
WLAN_HDD_TM_ATTR_DATA = 2,
WLAN_HDD_TM_ATTR_STREAM_ID = 3,
WLAN_HDD_TM_ATTR_TYPE = 4,
/* keep last */
WLAN_HDD_TM_ATTR_AFTER_LAST,
WLAN_HDD_TM_ATTR_MAX = WLAN_HDD_TM_ATTR_AFTER_LAST - 1,
};
enum wlan_hdd_tm_cmd {
WLAN_HDD_TM_CMD_WLAN_FTM = 0,
WLAN_HDD_TM_CMD_WLAN_HB = 1,
};
#define WLAN_HDD_TM_DATA_MAX_LEN 5000
static const struct nla_policy wlan_hdd_tm_policy[WLAN_HDD_TM_ATTR_MAX + 1] = {
[WLAN_HDD_TM_ATTR_CMD] = {.type = NLA_U32},
[WLAN_HDD_TM_ATTR_DATA] = {.type = NLA_BINARY,
.len = WLAN_HDD_TM_DATA_MAX_LEN},
};
#endif /* WLAN_NL80211_TESTMODE */
enum wlan_hdd_vendor_ie_access_policy {
WLAN_HDD_VENDOR_IE_ACCESS_NONE = 0,
WLAN_HDD_VENDOR_IE_ACCESS_ALLOW_IF_LISTED,
};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = {
.flags = WIPHY_WOWLAN_MAGIC_PKT,
.n_patterns = WOWL_MAX_PTRNS_ALLOWED,
.pattern_min_len = 1,
.pattern_max_len = WOWL_PTRN_MAX_SIZE,
};
#endif
/**
* hdd_add_channel_switch_support()- Adds Channel Switch flag if supported
* @flags: Pointer to the flags to Add channel switch flag.
*
* This Function adds Channel Switch support flag, if channel switch is
* supported by kernel.
* Return: void.
*/
#ifdef CHANNEL_SWITCH_SUPPORTED
static inline void hdd_add_channel_switch_support(uint32_t *flags)
{
*flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
}
#else
static inline void hdd_add_channel_switch_support(uint32_t *flags)
{
}
#endif
#ifdef FEATURE_WLAN_TDLS
/* TDLS capabilities params */
#define PARAM_MAX_TDLS_SESSION \
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS
#define PARAM_TDLS_FEATURE_SUPPORT \
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED
/**
* __wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function provides TDLS capabilities
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct sk_buff *skb;
uint32_t set = 0;
uint32_t max_num_tdls_sta = 0;
bool tdls_support;
bool tdls_external_control;
bool tdls_sleep_sta_enable;
bool tdls_buffer_sta;
bool tdls_off_channel;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, (2 * sizeof(u32)) +
NLMSG_HDRLEN);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
goto fail;
}
if ((cfg_tdls_get_support_enable(hdd_ctx->psoc, &tdls_support) ==
QDF_STATUS_SUCCESS) && !tdls_support) {
hdd_debug("TDLS feature not Enabled or Not supported in FW");
if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION, 0) ||
nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, 0)) {
hdd_err("nla put fail");
goto fail;
}
} else {
cfg_tdls_get_external_control(hdd_ctx->psoc,
&tdls_external_control);
cfg_tdls_get_sleep_sta_enable(hdd_ctx->psoc,
&tdls_sleep_sta_enable);
cfg_tdls_get_buffer_sta_enable(hdd_ctx->psoc,
&tdls_buffer_sta);
cfg_tdls_get_off_channel_enable(hdd_ctx->psoc,
&tdls_off_channel);
set = set | WIFI_TDLS_SUPPORT;
set = set | (tdls_external_control ?
WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT : 0);
set = set | (tdls_off_channel ?
WIIF_TDLS_OFFCHANNEL_SUPPORT : 0);
if (tdls_sleep_sta_enable || tdls_buffer_sta ||
tdls_off_channel)
max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA_P_UAPSD_OFFCHAN;
else
max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA;
hdd_debug("TDLS Feature supported value %x", set);
if (nla_put_u32(skb, PARAM_MAX_TDLS_SESSION,
max_num_tdls_sta) ||
nla_put_u32(skb, PARAM_TDLS_FEATURE_SUPPORT, set)) {
hdd_err("nla put fail");
goto fail;
}
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_tdls_capabilities() - Provide TDLS Capabilities.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function provides TDLS capabilities
*
* Return: 0 on success and errno on failure
*/
static int
wlan_hdd_cfg80211_get_tdls_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_tdls_capabilities(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
static uint8_t hdd_get_bw_offset(uint32_t ch_width)
{
uint8_t bw_offset = 0;
if (ch_width == CH_WIDTH_40MHZ)
bw_offset = 1 << BW_40_OFFSET_BIT;
else if (ch_width == CH_WIDTH_20MHZ)
bw_offset = 1 << BW_20_OFFSET_BIT;
return bw_offset;
}
#else /* !FEATURE_WLAN_TDLS */
static inline uint8_t hdd_get_bw_offset(uint32_t ch_width)
{
return 0;
}
#endif /* FEATURE_WLAN_TDLS */
#ifdef QCA_HT_2040_COEX
static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work);
#endif
int wlan_hdd_merge_avoid_freqs(struct ch_avoid_ind_type *destFreqList,
struct ch_avoid_ind_type *srcFreqList)
{
int i;
uint32_t room;
struct ch_avoid_freq_type *avoid_range =
&destFreqList->avoid_freq_range[destFreqList->ch_avoid_range_cnt];
room = CH_AVOID_MAX_RANGE - destFreqList->ch_avoid_range_cnt;
if (srcFreqList->ch_avoid_range_cnt > room) {
hdd_err("avoid freq overflow");
return -EINVAL;
}
destFreqList->ch_avoid_range_cnt += srcFreqList->ch_avoid_range_cnt;
for (i = 0; i < srcFreqList->ch_avoid_range_cnt; i++) {
avoid_range->start_freq =
srcFreqList->avoid_freq_range[i].start_freq;
avoid_range->end_freq =
srcFreqList->avoid_freq_range[i].end_freq;
avoid_range++;
}
return 0;
}
/*
* FUNCTION: wlan_hdd_send_avoid_freq_event
* This is called when wlan driver needs to send vendor specific
* avoid frequency range event to userspace
*/
int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx,
struct ch_avoid_ind_type *avoid_freq_list)
{
struct sk_buff *vendor_event;
hdd_enter();
if (!hdd_ctx) {
hdd_err("HDD context is null");
return -EINVAL;
}
if (!avoid_freq_list) {
hdd_err("avoid_freq_list is null");
return -EINVAL;
}
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL, sizeof(struct ch_avoid_ind_type),
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hdd_err("cfg80211_vendor_event_alloc failed");
return -EINVAL;
}
memcpy(skb_put(vendor_event, sizeof(struct ch_avoid_ind_type)),
(void *)avoid_freq_list, sizeof(struct ch_avoid_ind_type));
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
hdd_exit();
return 0;
}
/*
* define short names for the global vendor params
* used by QCA_NL80211_VENDOR_SUBCMD_HANG
*/
#define HANG_REASON_INDEX QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX
/**
* hdd_convert_hang_reason() - Convert cds recovery reason to vendor specific
* hang reason
* @reason: cds recovery reason
*
* Return: Vendor specific reason code
*/
static enum qca_wlan_vendor_hang_reason
hdd_convert_hang_reason(enum qdf_hang_reason reason)
{
u32 ret_val;
switch (reason) {
case QDF_RX_HASH_NO_ENTRY_FOUND:
ret_val = QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND;
break;
case QDF_PEER_DELETION_TIMEDOUT:
ret_val = QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT;
break;
case QDF_PEER_UNMAP_TIMEDOUT:
ret_val = QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT;
break;
case QDF_SCAN_REQ_EXPIRED:
ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED;
break;
case QDF_SCAN_ATTEMPT_FAILURES:
ret_val = QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES;
break;
case QDF_GET_MSG_BUFF_FAILURE:
ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE;
break;
case QDF_ACTIVE_LIST_TIMEOUT:
ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT;
break;
case QDF_SUSPEND_TIMEOUT:
ret_val = QCA_WLAN_HANG_SUSPEND_TIMEOUT;
break;
case QDF_RESUME_TIMEOUT:
ret_val = QCA_WLAN_HANG_RESUME_TIMEOUT;
break;
case QDF_WMI_EXCEED_MAX_PENDING_CMDS:
ret_val = QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS;
break;
case QDF_REASON_UNSPECIFIED:
default:
ret_val = QCA_WLAN_HANG_REASON_UNSPECIFIED;
}
return ret_val;
}
/**
* wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace
* @hdd_ctx: Pointer to hdd context
* @reason: cds recovery reason
*
* Return: 0 on success or failure reason
*/
int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx,
enum qdf_hang_reason reason)
{
struct sk_buff *vendor_event;
enum qca_wlan_vendor_hang_reason hang_reason;
hdd_enter();
if (!hdd_ctx) {
hdd_err("HDD context is null");
return -EINVAL;
}
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL,
sizeof(uint32_t),
HANG_REASON_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hdd_err("cfg80211_vendor_event_alloc failed");
return -ENOMEM;
}
hang_reason = hdd_convert_hang_reason(reason);
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON,
(uint32_t)hang_reason)) {
hdd_err("QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail");
kfree_skb(vendor_event);
return -EINVAL;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
hdd_exit();
return 0;
}
#undef HANG_REASON_INDEX
/**
* wlan_hdd_get_adjacent_chan(): Gets next/previous channel
* with respect to the channel passed.
* @chan: Channel
* @upper: If "true" then next channel is returned or else
* previous channel is returned.
*
* This function returns the next/previous adjacent-channel to
* the channel passed. If "upper = true" then next channel is
* returned else previous is returned.
*/
int wlan_hdd_get_adjacent_chan(uint8_t chan, bool upper)
{
enum channel_enum ch_idx = wlan_reg_get_chan_enum(chan);
if (ch_idx == INVALID_CHANNEL)
return -EINVAL;
if (upper && (ch_idx < (NUM_CHANNELS - 1)))
ch_idx++;
else if (!upper && (ch_idx > CHAN_ENUM_1))
ch_idx--;
else
return -EINVAL;
return WLAN_REG_CH_NUM(ch_idx);
}
/**
* wlan_hdd_send_avoid_freq_for_dnbs(): Sends list of frequencies to be
* avoided when Do_Not_Break_Stream is active.
* @hdd_ctx: HDD Context
* @op_chan: AP/P2P-GO operating channel
*
* This function sends list of frequencies to be avoided when
* Do_Not_Break_Stream is active.
* To clear the avoid_frequency_list in the application,
* op_chan = 0 can be passed.
*
* Return: 0 on success and errno on failure
*/
int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, uint8_t op_chan)
{
struct ch_avoid_ind_type p2p_avoid_freq_list;
uint8_t min_chan, max_chan;
int ret;
int chan;
hdd_enter();
if (!hdd_ctx) {
hdd_err("invalid param");
return -EINVAL;
}
qdf_mem_zero(&p2p_avoid_freq_list, sizeof(struct ch_avoid_ind_type));
/*
* If channel passed is zero, clear the avoid_freq list in application.
*/
if (!op_chan) {
#ifdef FEATURE_WLAN_CH_AVOID
mutex_lock(&hdd_ctx->avoid_freq_lock);
qdf_mem_zero(&hdd_ctx->dnbs_avoid_freq_list,
sizeof(struct ch_avoid_ind_type));
if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt)
memcpy(&p2p_avoid_freq_list,
&hdd_ctx->coex_avoid_freq_list,
sizeof(struct ch_avoid_ind_type));
mutex_unlock(&hdd_ctx->avoid_freq_lock);
#endif
ret = wlan_hdd_send_avoid_freq_event(hdd_ctx,
&p2p_avoid_freq_list);
if (ret)
hdd_err("wlan_hdd_send_avoid_freq_event error:%d",
ret);
return ret;
}
if (WLAN_REG_IS_24GHZ_CH(op_chan)) {
min_chan = WLAN_REG_MIN_24GHZ_CH_NUM;
max_chan = WLAN_REG_MAX_24GHZ_CH_NUM;
} else if (WLAN_REG_IS_5GHZ_CH(op_chan)) {
min_chan = WLAN_REG_MIN_5GHZ_CH_NUM;
max_chan = WLAN_REG_MAX_5GHZ_CH_NUM;
} else {
hdd_err("invalid channel:%d", op_chan);
return -EINVAL;
}
if ((op_chan > min_chan) && (op_chan < max_chan)) {
p2p_avoid_freq_list.ch_avoid_range_cnt = 2;
p2p_avoid_freq_list.avoid_freq_range[0].start_freq =
wlan_chan_to_freq(min_chan);
/* Get channel before the op_chan */
chan = wlan_hdd_get_adjacent_chan(op_chan, false);
if (chan < 0)
return -EINVAL;
p2p_avoid_freq_list.avoid_freq_range[0].end_freq =
wlan_chan_to_freq(chan);
/* Get channel next to the op_chan */
chan = wlan_hdd_get_adjacent_chan(op_chan, true);
if (chan < 0)
return -EINVAL;
p2p_avoid_freq_list.avoid_freq_range[1].start_freq =
wlan_chan_to_freq(chan);
p2p_avoid_freq_list.avoid_freq_range[1].end_freq =
wlan_chan_to_freq(max_chan);
} else if (op_chan == min_chan) {
p2p_avoid_freq_list.ch_avoid_range_cnt = 1;
chan = wlan_hdd_get_adjacent_chan(op_chan, true);
if (chan < 0)
return -EINVAL;
p2p_avoid_freq_list.avoid_freq_range[0].start_freq =
wlan_chan_to_freq(chan);
p2p_avoid_freq_list.avoid_freq_range[0].end_freq =
wlan_chan_to_freq(max_chan);
} else {
p2p_avoid_freq_list.ch_avoid_range_cnt = 1;
p2p_avoid_freq_list.avoid_freq_range[0].start_freq =
wlan_chan_to_freq(min_chan);
chan = wlan_hdd_get_adjacent_chan(op_chan, false);
if (chan < 0)
return -EINVAL;
p2p_avoid_freq_list.avoid_freq_range[0].end_freq =
wlan_chan_to_freq(chan);
}
#ifdef FEATURE_WLAN_CH_AVOID
mutex_lock(&hdd_ctx->avoid_freq_lock);
hdd_ctx->dnbs_avoid_freq_list = p2p_avoid_freq_list;
if (hdd_ctx->coex_avoid_freq_list.ch_avoid_range_cnt) {
ret = wlan_hdd_merge_avoid_freqs(&p2p_avoid_freq_list,
&hdd_ctx->coex_avoid_freq_list);
if (ret) {
mutex_unlock(&hdd_ctx->avoid_freq_lock);
hdd_err("avoid freq merge failed");
return ret;
}
}
mutex_unlock(&hdd_ctx->avoid_freq_lock);
#endif
ret = wlan_hdd_send_avoid_freq_event(hdd_ctx, &p2p_avoid_freq_list);
if (ret)
hdd_err("wlan_hdd_send_avoid_freq_event error:%d", ret);
return ret;
}
/* vendor specific events */
static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = {
[QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY
},
[QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_NAN
},
#ifdef WLAN_FEATURE_STATS_EXT
[QCA_NL80211_VENDOR_SUBCMD_STATS_EXT_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_STATS_EXT
},
#endif /* WLAN_FEATURE_STATS_EXT */
#ifdef FEATURE_WLAN_EXTSCAN
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd
=
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd
=
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd =
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SIGNIFICANT_CHANGE
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd
=
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_SIGNIFICANT_CHANGE
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE_INDEX] = {
.
vendor_id
=
QCA_NL80211_VENDOR_ID,
.
subcmd
=
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_SIGNIFICANT_CHANGE
},
#endif /* FEATURE_WLAN_EXTSCAN */
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
[QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET
},
[QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET
},
[QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR
},
[QCA_NL80211_VENDOR_SUBCMD_LL_RADIO_STATS_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS
},
[QCA_NL80211_VENDOR_SUBCMD_LL_IFACE_STATS_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS
},
[QCA_NL80211_VENDOR_SUBCMD_LL_PEER_INFO_STATS_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS
},
[QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT
},
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
[QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE
},
[QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS
},
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
[QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH
},
#endif
[QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED
},
[QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED
},
[QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED
},
[QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED
},
[QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX] = {
.vendor_id =
QCA_NL80211_VENDOR_ID,
.subcmd =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED
},
#ifdef FEATURE_WLAN_EXTSCAN
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_NETWORK_FOUND
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_PNO_PASSPOINT_NETWORK_FOUND
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST
},
#endif /* FEATURE_WLAN_EXTSCAN */
FEATURE_RSSI_MONITOR_VENDOR_EVENTS
#ifdef WLAN_FEATURE_TSF
[QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_TSF
},
#endif
[QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE
},
[QCA_NL80211_VENDOR_SUBCMD_SCAN_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
},
/* OCB events */
[QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT
},
#ifdef FEATURE_LFR_SUBNET_DETECTION
[QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG
},
#endif /*FEATURE_LFR_SUBNET_DETECTION */
[QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_NDP
},
[QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP
},
[QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH
},
[QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
},
[QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE
},
[QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET,
},
[QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_HANG,
},
[QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO,
},
[QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT,
},
[QCA_NL80211_VENDOR_SUBCMD_NAN_EXT_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN_EXT
},
[QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES,
},
#ifdef WLAN_UMAC_CONVERGENCE
COMMON_VENDOR_EVENTS
#endif
};
/**
* __is_driver_dfs_capable() - get driver DFS capability
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This function is called by userspace to indicate whether or not
* the driver supports DFS offload.
*
* Return: 0 on success, negative errno on failure
*/
static int __is_driver_dfs_capable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u32 dfs_capability = 0;
struct sk_buff *temp_skbuff;
int ret_val;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
hdd_enter_dev(wdev->netdev);
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \
defined(CFG80211_DFS_OFFLOAD_BACKPORT)
dfs_capability =
wiphy_ext_feature_isset(wiphy,
NL80211_EXT_FEATURE_DFS_OFFLOAD);
#else
dfs_capability = !!(wiphy->flags & WIPHY_FLAG_DFS_OFFLOAD);
#endif
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
NLMSG_HDRLEN);
if (temp_skbuff) {
ret_val = nla_put_u32(temp_skbuff, QCA_WLAN_VENDOR_ATTR_DFS,
dfs_capability);
if (ret_val) {
hdd_err("QCA_WLAN_VENDOR_ATTR_DFS put fail");
kfree_skb(temp_skbuff);
return ret_val;
}
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
hdd_err("dfs capability: buffer alloc fail");
return -ENOMEM;
}
/**
* is_driver_dfs_capable() - get driver DFS capability
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This function is called by userspace to indicate whether or not
* the driver supports DFS offload. This is an SSR-protected
* wrapper function.
*
* Return: 0 on success, negative errno on failure
*/
static int is_driver_dfs_capable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __is_driver_dfs_capable(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* wlan_hdd_sap_cfg_dfs_override() - DFS MCC restriction check
*
* @adapter: SAP adapter pointer
*
* DFS in MCC is not supported for Multi bssid SAP mode due to single physical
* radio. So in case of DFS MCC scenario override current SAP given config
* to follow concurrent SAP DFS config
*
* Return: 0 - No DFS issue, 1 - Override done and negative error codes
*/
int wlan_hdd_sap_cfg_dfs_override(struct hdd_adapter *adapter)
{
struct hdd_adapter *con_sap_adapter;
struct sap_config *sap_config, *con_sap_config;
int con_ch;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (!hdd_ctx) {
hdd_err("hdd context is NULL");
return 0;
}
/*
* Check if AP+AP case, once primary AP chooses a DFS
* channel secondary AP should always follow primary APs channel
*/
if (!policy_mgr_concurrent_beaconing_sessions_running(
hdd_ctx->psoc))
return 0;
con_sap_adapter = hdd_get_con_sap_adapter(adapter, true);
if (!con_sap_adapter)
return 0;
sap_config = &adapter->session.ap.sap_config;
con_sap_config = &con_sap_adapter->session.ap.sap_config;
con_ch = con_sap_adapter->session.ap.operating_channel;
if (!wlan_reg_is_dfs_ch(hdd_ctx->pdev, con_ch))
return 0;
hdd_debug("Only SCC AP-AP DFS Permitted (ch=%d, con_ch=%d)",
sap_config->channel, con_ch);
hdd_debug("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) {
hdd_err("Primary AP channel config error");
hdd_err("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)
qdf_mem_free(sap_config->acs_cfg.ch_list);
qdf_mem_copy(&sap_config->acs_cfg,
&con_sap_config->acs_cfg,
sizeof(struct sap_acs_cfg));
sap_config->acs_cfg.ch_list = qdf_mem_malloc(
sizeof(uint8_t) *
con_sap_config->acs_cfg.ch_list_count);
if (!sap_config->acs_cfg.ch_list)
return -ENOMEM;
qdf_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;
}
/**
* wlan_hdd_set_acs_ch_range : Populate ACS hw mode and channel range values
* @sap_cfg: pointer to SAP config struct
* @hw_mode: hw mode retrieved from vendor command buffer
* @ht_enabled: whether HT phy mode is enabled
* @vht_enabled: whether VHT phy mode is enabled
*
* This function populates the ACS hw mode based on the configuration retrieved
* from the vendor command buffer; and sets ACS start and end channel for the
* given band.
*
* Return: 0 if success; -EINVAL if ACS channel list is NULL
*/
static int wlan_hdd_set_acs_ch_range(
struct sap_config *sap_cfg, enum qca_wlan_vendor_acs_hw_mode hw_mode,
bool ht_enabled, bool vht_enabled)
{
int i;
if (hw_mode == QCA_ACS_MODE_IEEE80211B) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11b;
sap_cfg->acs_cfg.start_ch = WLAN_REG_CH_NUM(CHAN_ENUM_1);
sap_cfg->acs_cfg.end_ch = WLAN_REG_CH_NUM(CHAN_ENUM_14);
} else if (hw_mode == QCA_ACS_MODE_IEEE80211G) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11g;
sap_cfg->acs_cfg.start_ch = WLAN_REG_CH_NUM(CHAN_ENUM_1);
sap_cfg->acs_cfg.end_ch = WLAN_REG_CH_NUM(CHAN_ENUM_13);
} else if (hw_mode == QCA_ACS_MODE_IEEE80211A) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_11a;
sap_cfg->acs_cfg.start_ch = WLAN_REG_CH_NUM(CHAN_ENUM_36);
sap_cfg->acs_cfg.end_ch = WLAN_REG_CH_NUM(CHAN_ENUM_173);
} else if (hw_mode == QCA_ACS_MODE_IEEE80211ANY) {
sap_cfg->acs_cfg.hw_mode = eCSR_DOT11_MODE_abg;
sap_cfg->acs_cfg.start_ch = WLAN_REG_CH_NUM(CHAN_ENUM_1);
sap_cfg->acs_cfg.end_ch = WLAN_REG_CH_NUM(CHAN_ENUM_173);
}
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 -EINVAL;
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 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];
}
return 0;
}
static void wlan_hdd_cfg80211_start_pending_acs(struct work_struct *work);
static void hdd_update_acs_channel_list(struct sap_config *sap_config,
enum band_info band)
{
int i, temp_count = 0;
int acs_list_count = sap_config->acs_cfg.ch_list_count;
for (i = 0; i < acs_list_count; i++) {
if (BAND_2G == band) {
if (WLAN_REG_IS_24GHZ_CH(
sap_config->acs_cfg.ch_list[i])) {
sap_config->acs_cfg.ch_list[temp_count] =
sap_config->acs_cfg.ch_list[i];
temp_count++;
}
} else if (BAND_5G == band) {
if (WLAN_REG_IS_5GHZ_CH(
sap_config->acs_cfg.ch_list[i])) {
sap_config->acs_cfg.ch_list[temp_count] =
sap_config->acs_cfg.ch_list[i];
temp_count++;
}
}
}
sap_config->acs_cfg.ch_list_count = temp_count;
}
/**
* wlan_hdd_cfg80211_start_acs : Start ACS Procedure for SAP
* @adapter: pointer to SAP adapter struct
*
* This function starts the ACS procedure if there are no
* constraints like MBSSID DFS restrictions.
*
* Return: Status of ACS Start procedure
*/
int wlan_hdd_cfg80211_start_acs(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx;
struct sap_config *sap_config;
sap_event_cb acs_event_callback;
uint8_t mcc_to_scc_switch = 0;
int status;
if (!adapter) {
hdd_err("adapter is NULL");
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
sap_config = &adapter->session.ap.sap_config;
if (!sap_config) {
hdd_err("SAP config is NULL");
return -EINVAL;
}
if (hdd_ctx->acs_policy.acs_channel)
sap_config->channel = hdd_ctx->acs_policy.acs_channel;
else
sap_config->channel = AUTO_CHANNEL_SELECT;
ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc,
&mcc_to_scc_switch);
/*
* No DFS SCC is allowed in Auto use case. Hence not
* calling DFS override
*/
if (QDF_MCC_TO_SCC_SWITCH_FORCE_PREFERRED_WITHOUT_DISCONNECTION !=
mcc_to_scc_switch) {
status = wlan_hdd_sap_cfg_dfs_override(adapter);
if (status < 0)
return status;
if (status > 0) {
/*notify hostapd about channel override */
wlan_hdd_cfg80211_acs_ch_select_evt(adapter);
clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return 0;
}
}
/* When first 2 connections are on the same frequency band,
* then PCL would include only channels from the other
* frequency band on which no connections are active
*/
if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) &&
(sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) {
struct policy_mgr_conc_connection_info *conc_connection_info;
uint32_t i;
conc_connection_info = policy_mgr_get_conn_info(&i);
if (conc_connection_info[0].mac ==
conc_connection_info[1].mac) {
if (WLAN_REG_IS_5GHZ_CH(sap_config->acs_cfg.
pcl_channels[0])) {
sap_config->acs_cfg.band =
QCA_ACS_MODE_IEEE80211A;
hdd_update_acs_channel_list(sap_config,
BAND_5G);
} else {
sap_config->acs_cfg.band =
QCA_ACS_MODE_IEEE80211G;
hdd_update_acs_channel_list(sap_config,
BAND_2G);
}
}
}
status = wlan_hdd_config_acs(hdd_ctx, adapter);
if (status) {
hdd_err("ACS config failed");
return -EINVAL;
}
acs_event_callback = hdd_hostapd_sap_event_cb;
qdf_mem_copy(sap_config->self_macaddr.bytes,
adapter->mac_addr.bytes, sizeof(struct qdf_mac_addr));
hdd_info("ACS Started for %s", adapter->dev->name);
status = wlansap_acs_chselect(
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
acs_event_callback, sap_config, adapter->dev);
if (status) {
hdd_err("ACS channel select failed");
return -EINVAL;
}
if (sap_is_auto_channel_select(WLAN_HDD_GET_SAP_CTX_PTR(adapter)))
sap_config->acs_cfg.acs_mode = true;
set_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return 0;
}
/**
* hdd_update_vendor_pcl_list() - This API will return unsorted pcl list
* @hdd_ctx: hdd context
* @acs_chan_params: external acs channel params
* @sap_config: SAP config
*
* This API provides unsorted pcl list.
* this list is a subset of the valid channel list given by hostapd.
* if channel is not present in pcl, weightage will be given as zero
*
* Return: Zero on success, non-zero on failure
*/
static void hdd_update_vendor_pcl_list(struct hdd_context *hdd_ctx,
struct hdd_vendor_acs_chan_params *acs_chan_params,
struct sap_config *sap_config)
{
int i, j;
/*
* PCL shall contain only the preferred channels from the
* application. If those channels are not present in the
* driver PCL, then set the weight to zero
*/
for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) {
acs_chan_params->vendor_pcl_list[i] =
sap_config->acs_cfg.ch_list[i];
acs_chan_params->vendor_weight_list[i] = 0;
for (j = 0; j < sap_config->acs_cfg.pcl_ch_count; j++) {
if (sap_config->acs_cfg.ch_list[i] ==
sap_config->acs_cfg.pcl_channels[j]) {
acs_chan_params->vendor_weight_list[i] =
sap_config->
acs_cfg.pcl_channels_weight_list[j];
break;
}
}
}
acs_chan_params->pcl_count = sap_config->acs_cfg.ch_list_count;
}
/**
* hdd_update_reg_chan_info : This API contructs channel info
* for all the given channel
* @adapter: pointer to SAP adapter struct
* @channel_count: channel count
* @channel_list: channel list
*
* Return: Status of of channel information updation
*/
static int hdd_update_reg_chan_info(struct hdd_adapter *adapter,
uint32_t channel_count,
uint8_t *channel_list)
{
int i;
struct hdd_channel_info *icv;
struct ch_params ch_params = {0};
uint8_t bw_offset = 0, chan = 0;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct sap_config *sap_config = &adapter->session.ap.sap_config;
mac_handle_t mac_handle;
uint8_t sub_20_chan_width = 0;
QDF_STATUS status;
mac_handle = hdd_ctx->mac_handle;
sap_config->channel_info_count = channel_count;
status = ucfg_mlme_get_sub_20_chan_width(hdd_ctx->psoc,
&sub_20_chan_width);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to get sub_20_chan_width config");
for (i = 0; i < channel_count; i++) {
icv = &sap_config->channel_info[i];
chan = channel_list[i];
if (chan == 0)
continue;
icv->freq = wlan_reg_get_channel_freq(hdd_ctx->pdev, chan);
icv->ieee_chan_number = chan;
icv->max_reg_power = wlan_reg_get_channel_reg_power(
hdd_ctx->pdev, chan);
/* filling demo values */
icv->max_radio_power = HDD_MAX_TX_POWER;
icv->min_radio_power = HDD_MIN_TX_POWER;
/* not supported in current driver */
icv->max_antenna_gain = 0;
bw_offset = hdd_get_bw_offset(sap_config->acs_cfg.ch_width);
icv->reg_class_id =
wlan_hdd_find_opclass(mac_handle, chan, bw_offset);
if (WLAN_REG_IS_5GHZ_CH(chan)) {
ch_params.ch_width = sap_config->acs_cfg.ch_width;
wlan_reg_set_channel_params(hdd_ctx->pdev, chan,
0, &ch_params);
icv->vht_center_freq_seg0 = ch_params.center_freq_seg0;
icv->vht_center_freq_seg1 = ch_params.center_freq_seg1;
}
icv->flags = 0;
icv->flags = cds_get_vendor_reg_flags(hdd_ctx->pdev, chan,
sap_config->acs_cfg.ch_width,
sap_config->acs_cfg.is_ht_enabled,
sap_config->acs_cfg.is_vht_enabled,
sub_20_chan_width);
if (icv->flags & IEEE80211_CHAN_PASSIVE)
icv->flagext |= IEEE80211_CHAN_DFS;
hdd_debug("freq %d flags %d flagext %d ieee %d maxreg %d maxpw %d minpw %d regClass %d antenna %d seg0 %d seg1 %d",
icv->freq, icv->flags,
icv->flagext, icv->ieee_chan_number,
icv->max_reg_power, icv->max_radio_power,
icv->min_radio_power, icv->reg_class_id,
icv->max_antenna_gain, icv->vht_center_freq_seg0,
icv->vht_center_freq_seg1);
}
return 0;
}
/* Short name for QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO event */
#define CHAN_INFO_ATTR_FLAGS \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS
#define CHAN_INFO_ATTR_FLAG_EXT \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT
#define CHAN_INFO_ATTR_FREQ \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ
#define CHAN_INFO_ATTR_MAX_REG_POWER \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER
#define CHAN_INFO_ATTR_MAX_POWER \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER
#define CHAN_INFO_ATTR_MIN_POWER \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER
#define CHAN_INFO_ATTR_REG_CLASS_ID \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID
#define CHAN_INFO_ATTR_ANTENNA_GAIN \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN
#define CHAN_INFO_ATTR_VHT_SEG_0 \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0
#define CHAN_INFO_ATTR_VHT_SEG_1 \
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1
/**
* hdd_cfg80211_update_channel_info() - add channel info attributes
* @skb: pointer to sk buff
* @hdd_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_cfg80211_update_channel_info(struct sk_buff *skb,
struct sap_config *sap_config, int idx)
{
struct nlattr *nla_attr, *channel;
struct hdd_channel_info *icv;
int i;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
for (i = 0; i < sap_config->channel_info_count; i++) {
channel = nla_nest_start(skb, i);
if (!channel)
goto fail;
icv = &sap_config->channel_info[i];
if (!icv) {
hdd_err("channel info not found");
goto fail;
}
if (nla_put_u16(skb, CHAN_INFO_ATTR_FREQ,
icv->freq) ||
nla_put_u32(skb, CHAN_INFO_ATTR_FLAGS,
icv->flags) ||
nla_put_u32(skb, CHAN_INFO_ATTR_FLAG_EXT,
icv->flagext) ||
nla_put_u8(skb, CHAN_INFO_ATTR_MAX_REG_POWER,
icv->max_reg_power) ||
nla_put_u8(skb, CHAN_INFO_ATTR_MAX_POWER,
icv->max_radio_power) ||
nla_put_u8(skb, CHAN_INFO_ATTR_MIN_POWER,
icv->min_radio_power) ||
nla_put_u8(skb, CHAN_INFO_ATTR_REG_CLASS_ID,
icv->reg_class_id) ||
nla_put_u8(skb, CHAN_INFO_ATTR_ANTENNA_GAIN,
icv->max_antenna_gain) ||
nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_0,
icv->vht_center_freq_seg0) ||
nla_put_u8(skb, CHAN_INFO_ATTR_VHT_SEG_1,
icv->vht_center_freq_seg1)) {
hdd_err("put fail");
goto fail;
}
nla_nest_end(skb, channel);
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
hdd_err("nl channel update failed");
return -EINVAL;
}
#undef CHAN_INFO_ATTR_FLAGS
#undef CHAN_INFO_ATTR_FLAG_EXT
#undef CHAN_INFO_ATTR_FREQ
#undef CHAN_INFO_ATTR_MAX_REG_POWER
#undef CHAN_INFO_ATTR_MAX_POWER
#undef CHAN_INFO_ATTR_MIN_POWER
#undef CHAN_INFO_ATTR_REG_CLASS_ID
#undef CHAN_INFO_ATTR_ANTENNA_GAIN
#undef CHAN_INFO_ATTR_VHT_SEG_0
#undef CHAN_INFO_ATTR_VHT_SEG_1
/**
* hdd_cfg80211_update_pcl() - add pcl info attributes
* @skb: pointer to sk buff
* @hdd_ctx: pointer to hdd station context
* @idx: attribute index
* @vendor_pcl_list: PCL list
* @vendor_weight_list: PCL weights
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_cfg80211_update_pcl(struct sk_buff *skb,
uint8_t ch_list_count, int idx,
uint8_t *vendor_pcl_list, uint8_t *vendor_weight_list)
{
struct nlattr *nla_attr, *channel;
int i;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
for (i = 0; i < ch_list_count; i++) {
channel = nla_nest_start(skb, i);
if (!channel)
goto fail;
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL,
vendor_pcl_list[i]) ||
nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT,
vendor_weight_list[i])) {
hdd_err("put fail");
goto fail;
}
nla_nest_end(skb, channel);
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
hdd_err("updating pcl list failed");
return -EINVAL;
}
static void hdd_get_scan_band(struct hdd_context *hdd_ctx,
struct sap_config *sap_config,
enum band_info *band)
{
/* Get scan band */
if ((sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211B) ||
(sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211G)) {
*band = BAND_2G;
} else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211A) {
*band = BAND_5G;
} else if (sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY) {
*band = BAND_ALL;
}
}
/**
* hdd_get_freq_list: API to get Frequency list based on channel list
* @channel_list: channel list
* @freq_list: frequency list
* @channel_count: channel count
*
* Return: None
*/
static void hdd_get_freq_list(uint8_t *channel_list, uint32_t *freq_list,
uint32_t channel_count)
{
int count;
for (count = 0; count < channel_count ; count++)
freq_list[count] = cds_chan_to_freq(channel_list[count]);
}
/**
* wlan_hdd_sap_get_valid_channellist() - Get SAPs valid channel list
* @ap_adapter: adapter
* @channel_count: valid channel count
* @channel_list: valid channel list
* @band: frequency band
*
* This API returns valid channel list for SAP after removing nol and
* channel which lies outside of configuration.
*
* Return: Zero on success, non-zero on failure
*/
static int wlan_hdd_sap_get_valid_channellist(struct hdd_adapter *adapter,
uint32_t *channel_count,
uint8_t *channel_list,
enum band_info band)
{
struct sap_config *sap_config;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t tmp_chan_list[QDF_MAX_NUM_CHAN] = {0};
uint32_t chan_count;
uint8_t i;
QDF_STATUS status;
struct wlan_objmgr_pdev *pdev = hdd_ctx->pdev;
uint8_t tmp_chan;
sap_config = &adapter->session.ap.sap_config;
status =
policy_mgr_get_valid_chans(hdd_ctx->psoc,
tmp_chan_list,
&chan_count);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to get channel list");
return -EINVAL;
}
for (i = 0; i < chan_count; i++) {
tmp_chan = tmp_chan_list[i];
if (*channel_count < QDF_MAX_NUM_CHAN) {
if ((band == BAND_2G) &&
(WLAN_REG_IS_24GHZ_CH(tmp_chan)) &&
(!wlan_reg_is_disable_ch(pdev, tmp_chan))) {
channel_list[*channel_count] = tmp_chan;
*channel_count += 1;
} else if ((band == BAND_5G) &&
(WLAN_REG_IS_5GHZ_CH(tmp_chan)) &&
(!wlan_reg_is_disable_ch(pdev, tmp_chan))) {
channel_list[*channel_count] = tmp_chan;
*channel_count += 1;
}
} else {
break;
}
}
if (*channel_count == 0) {
hdd_err("no valid channel found");
return -EINVAL;
}
return 0;
}
int hdd_cfg80211_update_acs_config(struct hdd_adapter *adapter,
uint8_t reason)
{
struct sk_buff *skb;
struct sap_config *sap_config;
uint32_t channel_count = 0, status = -EINVAL;
uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0};
uint32_t freq_list[QDF_MAX_NUM_CHAN] = {0};
uint8_t vendor_pcl_list[QDF_MAX_NUM_CHAN] = {0};
uint8_t vendor_weight_list[QDF_MAX_NUM_CHAN] = {0};
struct hdd_vendor_acs_chan_params acs_chan_params;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
enum band_info band = BAND_2G;
eCsrPhyMode phy_mode;
enum qca_wlan_vendor_attr_external_acs_policy acs_policy;
uint32_t i;
QDF_STATUS qdf_status;
bool is_external_acs_policy = cfg_default(CFG_EXTERNAL_ACS_POLICY);
if (!hdd_ctx) {
hdd_err("HDD context is NULL");
return -EINVAL;
}
hdd_enter();
sap_config = &adapter->session.ap.sap_config;
/* When first 2 connections are on the same frequency band,
* then PCL would include only channels from the other
* frequency band on which no connections are active
*/
if ((policy_mgr_get_connection_count(hdd_ctx->psoc) == 2) &&
(sap_config->acs_cfg.band == QCA_ACS_MODE_IEEE80211ANY)) {
struct policy_mgr_conc_connection_info *conc_connection_info;
conc_connection_info = policy_mgr_get_conn_info(&i);
if (conc_connection_info[0].mac ==
conc_connection_info[1].mac) {
if (WLAN_REG_IS_5GHZ_CH(sap_config->acs_cfg.
pcl_channels[0])) {
sap_config->acs_cfg.band =
QCA_ACS_MODE_IEEE80211A;
hdd_update_acs_channel_list(sap_config,
BAND_5G);
} else {
sap_config->acs_cfg.band =
QCA_ACS_MODE_IEEE80211G;
hdd_update_acs_channel_list(sap_config,
BAND_2G);
}
}
}
hdd_get_scan_band(hdd_ctx, &adapter->session.ap.sap_config, &band);
if (sap_config->acs_cfg.ch_list) {
/* Copy INI or hostapd provided ACS channel range*/
qdf_mem_copy(channel_list, sap_config->acs_cfg.ch_list,
sap_config->acs_cfg.ch_list_count);
channel_count = sap_config->acs_cfg.ch_list_count;
} else {
/* No channel list provided, copy all valid channels */
wlan_hdd_sap_get_valid_channellist(adapter,
&channel_count,
channel_list,
band);
}
sap_config->channel_info = qdf_mem_malloc(
sizeof(struct hdd_channel_info) *
channel_count);
if (!sap_config->channel_info)
return -ENOMEM;
hdd_update_reg_chan_info(adapter, channel_count, channel_list);
hdd_get_freq_list(channel_list, freq_list, channel_count);
/* Get phymode */
phy_mode = adapter->session.ap.sap_config.acs_cfg.hw_mode;
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
&(adapter->wdev),
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_UPDATE_EXTERNAL_ACS_CONFIG,
GFP_KERNEL);
if (!skb) {
hdd_err("cfg80211_vendor_event_alloc failed");
qdf_mem_free(sap_config->channel_info);
return -ENOMEM;
}
/*
* Application expects pcl to be a subset of channel list
* Remove all channels which are not in channel list from pcl
* and add weight as zero
*/
acs_chan_params.channel_count = channel_count;
acs_chan_params.channel_list = channel_list;
acs_chan_params.vendor_pcl_list = vendor_pcl_list;
acs_chan_params.vendor_weight_list = vendor_weight_list;
hdd_update_vendor_pcl_list(hdd_ctx, &acs_chan_params,
sap_config);
if (acs_chan_params.channel_count) {
hdd_debug("ACS channel list: len: %d",
acs_chan_params.channel_count);
for (i = 0; i < acs_chan_params.channel_count; i++)
hdd_debug("%d ", acs_chan_params.channel_list[i]);
}
if (acs_chan_params.pcl_count) {
hdd_debug("ACS PCL list: len: %d",
acs_chan_params.pcl_count);
for (i = 0; i < acs_chan_params.pcl_count; i++)
hdd_debug("channel:%d, weight:%d ",
acs_chan_params.
vendor_pcl_list[i],
acs_chan_params.
vendor_weight_list[i]);
}
qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc,
&is_external_acs_policy);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("get_external_acs_policy failed, set default");
if (is_external_acs_policy) {
acs_policy =
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY;
} else {
acs_policy =
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED;
}
/* Update values in NL buffer */
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON,
reason) ||
nla_put_flag(skb,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED) ||
nla_put_flag(skb,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT)
||
nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH,
sap_config->acs_cfg.ch_width) ||
nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND,
band) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE,
phy_mode) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST,
channel_count * sizeof(uint32_t), freq_list)) {
hdd_err("nla put fail");
goto fail;
}
status =
hdd_cfg80211_update_pcl(skb,
acs_chan_params.
pcl_count,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL,
vendor_pcl_list,
vendor_weight_list);
if (status != 0)
goto fail;
status = hdd_cfg80211_update_channel_info(skb, sap_config,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO);
if (status != 0)
goto fail;
status = nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY,
acs_policy);
if (status != 0)
goto fail;
cfg80211_vendor_event(skb, GFP_KERNEL);
qdf_mem_free(sap_config->channel_info);
return 0;
fail:
qdf_mem_free(sap_config->channel_info);
if (skb)
kfree_skb(skb);
return status;
}
/**
* hdd_create_acs_timer(): Initialize vendor ACS timer
* @adapter: pointer to SAP adapter struct
*
* This function initializes the vendor ACS timer.
*
* Return: Status of create vendor ACS timer
*/
static int hdd_create_acs_timer(struct hdd_adapter *adapter)
{
struct hdd_external_acs_timer_context *timer_context;
QDF_STATUS status;
if (adapter->session.ap.vendor_acs_timer_initialized)
return 0;
hdd_debug("Starting vendor app based ACS");
timer_context = qdf_mem_malloc(sizeof(*timer_context));
if (!timer_context)
return -ENOMEM;
timer_context->adapter = adapter;
set_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
status = qdf_mc_timer_init(&adapter->session.ap.vendor_acs_timer,
QDF_TIMER_TYPE_SW,
hdd_acs_response_timeout_handler, timer_context);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to initialize acs response timeout timer");
return -EFAULT;
}
adapter->session.ap.vendor_acs_timer_initialized = true;
return 0;
}
static const struct nla_policy
wlan_hdd_cfg80211_do_acs_policy[QCA_WLAN_VENDOR_ATTR_ACS_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED] = { .type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST] = { .type = NLA_UNSPEC },
[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST] = { .type = NLA_UNSPEC },
};
int hdd_start_vendor_acs(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int status;
QDF_STATUS qdf_status;
bool is_acs_support_for_dfs_ltecoex = cfg_default(CFG_USER_ACS_DFS_LTE);
status = hdd_create_acs_timer(adapter);
if (status != 0) {
hdd_err("failed to create acs timer");
return status;
}
status = hdd_update_acs_timer_reason(adapter,
QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT);
if (status != 0) {
hdd_err("failed to update acs timer reason");
return status;
}
qdf_status = ucfg_mlme_get_acs_support_for_dfs_ltecoex(
hdd_ctx->psoc,
&is_acs_support_for_dfs_ltecoex);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("get_acs_support_for_dfs_ltecoex failed, set def");
if (is_acs_support_for_dfs_ltecoex)
status = qdf_status_to_os_return(wlan_sap_set_vendor_acs(
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
true));
else
status = qdf_status_to_os_return(wlan_sap_set_vendor_acs(
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
false));
return status;
}
/**
* __wlan_hdd_cfg80211_do_acs(): CFG80211 handler function for DO_ACS Vendor CMD
* @wiphy: Linux wiphy struct pointer
* @wdev: Linux wireless device struct pointer
* @data: ACS information from hostapd
* @data_len: ACS information length
*
* This function handle DO_ACS Vendor command from hostapd, parses ACS config
* and starts ACS procedure.
*
* Return: ACS procedure start status
*/
static int __wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *ndev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct sap_config *sap_config;
struct sk_buff *temp_skbuff;
int ret, i, ch_cnt = 0;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
bool ht_enabled, ht40_enabled, vht_enabled;
uint8_t ch_width;
enum qca_wlan_vendor_acs_hw_mode hw_mode;
enum policy_mgr_con_mode pm_mode;
QDF_STATUS qdf_status;
bool skip_etsi13_srd_chan = false;
bool is_vendor_acs_support =
cfg_default(CFG_USER_AUTO_CHANNEL_SELECTION);
bool is_external_acs_policy =
cfg_default(CFG_EXTERNAL_ACS_POLICY);
bool sap_force_11n_for_11ac = 0;
bool go_force_11n_for_11ac = 0;
bool etsi13_srd_chan;
bool go_11ac_override = 0;
bool sap_11ac_override = 0;
/* ***Note*** Donot set SME config related to ACS operation here because
* ACS operation is not synchronouse and ACS for Second AP may come when
* ACS operation for first AP is going on. So only do_acs is split to
* separate start_acs routine. Also SME-PMAC struct that is used to
* pass paremeters from HDD to SAP is global. Thus All ACS related SME
* config shall be set only from start_acs.
*/
hdd_enter_dev(ndev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
ucfg_mlme_get_sap_force_11n_for_11ac(hdd_ctx->psoc,
&sap_force_11n_for_11ac);
ucfg_mlme_get_go_force_11n_for_11ac(hdd_ctx->psoc,
&go_force_11n_for_11ac);
hdd_debug("current country is %s", hdd_ctx->reg.alpha2);
if (!((adapter->device_mode == QDF_SAP_MODE) ||
(adapter->device_mode == QDF_P2P_GO_MODE))) {
hdd_err("Invalid device mode %d", adapter->device_mode);
return -EINVAL;
}
if (cds_is_sub_20_mhz_enabled()) {
hdd_err("ACS not supported in sub 20 MHz ch wd.");
return -EINVAL;
}
if (qdf_atomic_read(&adapter->session.ap.acs_in_progress) > 0) {
hdd_err("ACS rejected as previous req already in progress");
return -EINVAL;
} else {
qdf_atomic_set(&adapter->session.ap.acs_in_progress, 1);
}
ret = wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, data,
data_len,
wlan_hdd_cfg80211_do_acs_policy);
if (ret) {
hdd_err("Invalid ATTR");
goto out;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
hdd_err("Attr hw_mode failed");
ret = -EINVAL;
goto out;
}
hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
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;
hdd_debug("ht40_enabled %d", ht40_enabled);
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 (((adapter->device_mode == QDF_SAP_MODE) &&
sap_force_11n_for_11ac) ||
((adapter->device_mode == QDF_P2P_GO_MODE) &&
go_force_11n_for_11ac)) {
vht_enabled = 0;
hdd_info("VHT is Disabled in ACS");
}
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]) {
ch_width = nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
} else {
if (ht_enabled && ht40_enabled)
ch_width = 40;
else
ch_width = 20;
}
/* this may be possible, when sap_force_11n_for_11ac or
* go_force_11n_for_11ac is set
*/
if ((ch_width == 80 || ch_width == 160) && !vht_enabled) {
if (ht_enabled && ht40_enabled)
ch_width = 40;
else
ch_width = 20;
}
sap_config = &adapter->session.ap.sap_config;
/* Check and free if memory is already allocated for acs channel list */
wlan_hdd_undo_acs(adapter);
qdf_mem_zero(&sap_config->acs_cfg, sizeof(struct sap_acs_cfg));
hdd_debug("channel width =%d", ch_width);
if (ch_width == 160)
sap_config->acs_cfg.ch_width = CH_WIDTH_160MHZ;
else if (ch_width == 80)
sap_config->acs_cfg.ch_width = CH_WIDTH_80MHZ;
else if (ch_width == 40)
sap_config->acs_cfg.ch_width = CH_WIDTH_40MHZ;
else
sap_config->acs_cfg.ch_width = CH_WIDTH_20MHZ;
/* hw_mode = a/b/g: QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST and
* QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST attrs are present, and
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is used for obtaining the
* channel list, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST is ignored
* since it contains the frequency values of the channels in
* the channel list.
* hw_mode = any: only QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST attr
* is present
*/
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 = qdf_mem_malloc(
sizeof(uint8_t) *
sap_config->acs_cfg.ch_list_count);
if (!sap_config->acs_cfg.ch_list) {
ret = -ENOMEM;
goto out;
}
qdf_mem_copy(sap_config->acs_cfg.ch_list, tmp,
sap_config->acs_cfg.ch_list_count);
}
} else if (tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) {
uint32_t *freq =
nla_data(tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]);
sap_config->acs_cfg.ch_list_count = nla_len(
tb[QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST]) /
sizeof(uint32_t);
if (sap_config->acs_cfg.ch_list_count) {
sap_config->acs_cfg.ch_list = qdf_mem_malloc(
sap_config->acs_cfg.ch_list_count);
if (!sap_config->acs_cfg.ch_list) {
ret = -ENOMEM;
goto out;
}
/* convert frequency to channel */
for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++)
sap_config->acs_cfg.ch_list[i] =
ieee80211_frequency_to_channel(freq[i]);
}
}
if (!sap_config->acs_cfg.ch_list_count) {
hdd_err("acs config chan count 0");
ret = -EINVAL;
goto out;
}
ucfg_mlme_get_etsi13_srd_chan_in_master_mode(hdd_ctx->psoc,
&etsi13_srd_chan);
skip_etsi13_srd_chan =
!etsi13_srd_chan &&
wlan_reg_is_etsi13_regdmn(hdd_ctx->pdev);
if (skip_etsi13_srd_chan) {
for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++) {
if (wlan_reg_is_etsi13_srd_chan(hdd_ctx->pdev,
sap_config->acs_cfg.
ch_list[i]))
sap_config->acs_cfg.ch_list[i] = 0;
else
sap_config->acs_cfg.ch_list[ch_cnt++] =
sap_config->acs_cfg.ch_list[i];
}
sap_config->acs_cfg.ch_list_count = ch_cnt;
}
hdd_debug("get pcl for DO_ACS vendor command");
pm_mode =
policy_mgr_convert_device_mode_to_qdf_type(adapter->device_mode);
/* consult policy manager to get PCL */
qdf_status = policy_mgr_get_pcl(hdd_ctx->psoc, pm_mode,
sap_config->acs_cfg.pcl_channels,
&sap_config->acs_cfg.pcl_ch_count,
sap_config->acs_cfg.
pcl_channels_weight_list,
QDF_MAX_NUM_CHAN);
if (qdf_status != QDF_STATUS_SUCCESS)
hdd_err("Get PCL failed");
if (sap_config->acs_cfg.pcl_ch_count) {
hdd_debug("ACS config PCL: len: %d",
sap_config->acs_cfg.pcl_ch_count);
for (i = 0; i < sap_config->acs_cfg.pcl_ch_count; i++)
hdd_debug("channel:%d, weight:%d ",
sap_config->acs_cfg.
pcl_channels[i],
sap_config->acs_cfg.
pcl_channels_weight_list[i]);
}
sap_config->acs_cfg.band = hw_mode;
qdf_status = ucfg_mlme_get_external_acs_policy(hdd_ctx->psoc,
&is_external_acs_policy);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("get_external_acs_policy failed");
if (is_external_acs_policy &&
policy_mgr_is_force_scc(hdd_ctx->psoc) &&
policy_mgr_get_connection_count(hdd_ctx->psoc)) {
policy_mgr_trim_acs_channel_list(
sap_config->acs_cfg.pcl_channels,
sap_config->acs_cfg.pcl_ch_count,
sap_config->acs_cfg.ch_list,
&sap_config->acs_cfg.ch_list_count);
/* if it is only one channel, send ACS event to upper layer */
if (sap_config->acs_cfg.ch_list_count == 1) {
sap_config->acs_cfg.pri_ch =
sap_config->acs_cfg.ch_list[0];
wlan_sap_set_sap_ctx_acs_cfg(
WLAN_HDD_GET_SAP_CTX_PTR(adapter), sap_config);
sap_config_acs_result(hdd_ctx->mac_handle,
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
sap_config->acs_cfg.ht_sec_ch);
sap_config->ch_params.ch_width =
sap_config->acs_cfg.ch_width;
sap_config->ch_params.sec_ch_offset =
sap_config->acs_cfg.ht_sec_ch;
sap_config->ch_params.center_freq_seg0 =
sap_config->acs_cfg.vht_seg0_center_ch;
sap_config->ch_params.center_freq_seg1 =
sap_config->acs_cfg.vht_seg1_center_ch;
/*notify hostapd about channel override */
wlan_hdd_cfg80211_acs_ch_select_evt(adapter);
ret = 0;
goto out;
}
}
ret = wlan_hdd_set_acs_ch_range(sap_config, hw_mode,
ht_enabled, vht_enabled);
if (ret) {
hdd_err("set acs channel range failed");
goto out;
}
ucfg_mlme_is_go_11ac_override(hdd_ctx->psoc, &go_11ac_override);
ucfg_mlme_is_sap_11ac_override(hdd_ctx->psoc, &sap_11ac_override);
/* ACS override for android */
if (ht_enabled &&
sap_config->acs_cfg.end_ch >= WLAN_REG_CH_NUM(CHAN_ENUM_36) &&
((adapter->device_mode == QDF_SAP_MODE &&
!sap_force_11n_for_11ac &&
sap_11ac_override) ||
(adapter->device_mode == QDF_P2P_GO_MODE &&
!go_force_11n_for_11ac &&
go_11ac_override))) {
vht_enabled = 1;
sap_config->acs_cfg.hw_mode = eCSR_DOT11_MODE_11ac;
qdf_status =
ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc,
&ch_width);
sap_config->acs_cfg.ch_width = ch_width;
}
/* 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;
hdd_debug("resetting to 40Mhz in 2.4Ghz");
}
hdd_debug("ACS Config for %s: HW_MODE: %d ACS_BW: %d HT: %d VHT: %d START_CH: %d END_CH: %d band %d",
adapter->dev->name, sap_config->acs_cfg.hw_mode,
sap_config->acs_cfg.ch_width, ht_enabled, vht_enabled,
sap_config->acs_cfg.start_ch, sap_config->acs_cfg.end_ch,
sap_config->acs_cfg.band);
host_log_acs_req_event(adapter->dev->name,
csr_phy_mode_str(sap_config->acs_cfg.hw_mode),
ch_width, ht_enabled, vht_enabled,
sap_config->acs_cfg.start_ch,
sap_config->acs_cfg.end_ch);
sap_config->acs_cfg.is_ht_enabled = ht_enabled;
sap_config->acs_cfg.is_vht_enabled = vht_enabled;
if (sap_config->acs_cfg.ch_list_count) {
hdd_debug("ACS channel list: len: %d",
sap_config->acs_cfg.ch_list_count);
for (i = 0; i < sap_config->acs_cfg.ch_list_count; i++)
hdd_debug("%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);
hdd_debug("ACS Pending for %s", adapter->dev->name);
ret = 0;
} else {
qdf_status =
ucfg_mlme_get_vendor_acs_support(
hdd_ctx->psoc,
&is_vendor_acs_support);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_err("get_vendor_acs_support failed, set default");
/* Check if vendor specific acs is enabled */
if (is_vendor_acs_support)
ret = hdd_start_vendor_acs(adapter);
else
ret = wlan_hdd_cfg80211_start_acs(adapter);
}
out:
if (ret == 0) {
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
NLMSG_HDRLEN);
if (temp_skbuff)
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
qdf_atomic_set(&adapter->session.ap.acs_in_progress, 0);
wlan_hdd_undo_acs(adapter);
clear_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags);
return ret;
}
/**
* wlan_hdd_cfg80211_do_acs : CFG80211 handler function for DO_ACS Vendor CMD
* @wiphy: Linux wiphy struct pointer
* @wdev: Linux wireless device struct pointer
* @data: ACS information from hostapd
* @data_len: ACS information len
*
* This function handle DO_ACS Vendor command from hostapd, parses ACS config
* and starts ACS procedure.
*
* Return: ACS procedure start status
*/
static int wlan_hdd_cfg80211_do_acs(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_do_acs(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_undo_acs : Do cleanup of DO_ACS
* @adapter: Pointer to adapter struct
*
* This function handle cleanup of what was done in DO_ACS, including free
* memory.
*
* Return: void
*/
void wlan_hdd_undo_acs(struct hdd_adapter *adapter)
{
if (!adapter)
return;
if (adapter->session.ap.sap_config.acs_cfg.ch_list) {
qdf_mem_free(adapter->session.ap.sap_config.acs_cfg.ch_list);
adapter->session.ap.sap_config.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)
{
struct hdd_adapter *adapter = container_of(work, struct hdd_adapter,
acs_pending_work.work);
struct osif_vdev_sync *vdev_sync;
if (osif_vdev_sync_op_start(adapter->dev, &vdev_sync))
return;
wlan_hdd_cfg80211_start_acs(adapter);
clear_bit(ACS_PENDING, &adapter->event_flags);
osif_vdev_sync_op_stop(vdev_sync);
}
/**
* 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(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct sap_config *sap_cfg =
&(WLAN_HDD_GET_AP_CTX_PTR(adapter))->sap_config;
struct sk_buff *vendor_event;
int ret_val;
struct hdd_adapter *con_sap_adapter;
uint16_t ch_width;
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
&(adapter->wdev),
4 * sizeof(u8) + 1 * sizeof(u16) + 4 + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_DO_ACS_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hdd_err("cfg80211_vendor_event_alloc failed");
return;
}
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
sap_cfg->acs_cfg.pri_ch);
if (ret_val) {
hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL put fail");
kfree_skb(vendor_event);
return;
}
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
sap_cfg->acs_cfg.ht_sec_ch);
if (ret_val) {
hdd_err("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) {
hdd_err("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) {
hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL put fail");
kfree_skb(vendor_event);
return;
}
if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_160MHZ)
ch_width = 160;
else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_80MHZ)
ch_width = 80;
else if (sap_cfg->acs_cfg.ch_width == CH_WIDTH_40MHZ)
ch_width = 40;
else
ch_width = 20;
ret_val = nla_put_u16(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
ch_width);
if (ret_val) {
hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH put fail");
kfree_skb(vendor_event);
return;
}
if (sap_cfg->acs_cfg.pri_ch > 14)
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
QCA_ACS_MODE_IEEE80211A);
else
ret_val = nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
QCA_ACS_MODE_IEEE80211G);
if (ret_val) {
hdd_err("QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE put fail");
kfree_skb(vendor_event);
return;
}
hdd_debug("ACS result for %s: PRI_CH: %d SEC_CH: %d VHT_SEG0: %d VHT_SEG1: %d ACS_BW: %d",
adapter->dev->name, 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 parameters. 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)) {
INIT_DELAYED_WORK(&con_sap_adapter->acs_pending_work,
wlan_hdd_cfg80211_start_pending_acs);
/* Lets give 1500ms for OBSS + START_BSS to complete */
schedule_delayed_work(&con_sap_adapter->acs_pending_work,
msecs_to_jiffies(1500));
}
}
/**
* hdd_is_wlm_latency_manager_supported - Checks if WLM Latency manager is
* supported
* @hdd_ctx: The HDD context
*
* Return: True if supported, false otherwise
*/
static inline
bool hdd_is_wlm_latency_manager_supported(struct hdd_context *hdd_ctx)
{
bool latency_enable;
if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_latency_enable
(hdd_ctx->psoc, &latency_enable)))
return false;
if (latency_enable &&
sme_is_feature_supported_by_fw(VDEV_LATENCY_CONFIG))
return true;
else
return false;
}
static int
__wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct sk_buff *skb = NULL;
uint32_t fset = 0;
int ret;
#ifdef FEATURE_WLAN_TDLS
bool bvalue;
#endif
/* ENTER_DEV() intentionally not used in a frequently invoked API */
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
hdd_debug("Infra Station mode is supported by driver");
fset |= WIFI_FEATURE_INFRA;
}
if (true == hdd_is_5g_supported(hdd_ctx)) {
hdd_debug("INFRA_5G is supported by firmware");
fset |= WIFI_FEATURE_INFRA_5G;
}
#ifdef WLAN_FEATURE_P2P
if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) &&
(wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO))) {
hdd_debug("WiFi-Direct is supported by driver");
fset |= WIFI_FEATURE_P2P;
}
#endif
fset |= WIFI_FEATURE_SOFT_AP;
/* HOTSPOT is a supplicant feature, enable it by default */
fset |= WIFI_FEATURE_HOTSPOT;
if (ucfg_extscan_get_enable(hdd_ctx->psoc) &&
sme_is_feature_supported_by_fw(EXTENDED_SCAN)) {
hdd_debug("EXTScan is supported by firmware");
fset |= WIFI_FEATURE_EXTSCAN | WIFI_FEATURE_HAL_EPNO;
}
if (wlan_hdd_nan_is_supported(hdd_ctx)) {
hdd_debug("NAN is supported by firmware");
fset |= WIFI_FEATURE_NAN;
}
if (sme_is_feature_supported_by_fw(RTT) &&
hdd_ctx->config->enable_rtt_support) {
hdd_debug("RTT is supported by firmware and framework");
fset |= WIFI_FEATURE_D2D_RTT;
fset |= WIFI_FEATURE_D2AP_RTT;
}
#ifdef FEATURE_WLAN_SCAN_PNO
if (ucfg_scan_get_pno_scan_support(hdd_ctx->psoc) &&
sme_is_feature_supported_by_fw(PNO)) {
hdd_debug("PNO is supported by firmware");
fset |= WIFI_FEATURE_PNO;
}
#endif
fset |= WIFI_FEATURE_ADDITIONAL_STA;
#ifdef FEATURE_WLAN_TDLS
cfg_tdls_get_support_enable(hdd_ctx->psoc, &bvalue);
if ((bvalue) && sme_is_feature_supported_by_fw(TDLS)) {
hdd_debug("TDLS is supported by firmware");
fset |= WIFI_FEATURE_TDLS;
}
cfg_tdls_get_off_channel_enable(hdd_ctx->psoc, &bvalue);
if (sme_is_feature_supported_by_fw(TDLS) &&
bvalue && sme_is_feature_supported_by_fw(TDLS_OFF_CHANNEL)) {
hdd_debug("TDLS off-channel is supported by firmware");
fset |= WIFI_FEATURE_TDLS_OFFCHANNEL;
}
#endif
fset |= WIFI_FEATURE_AP_STA;
fset |= WIFI_FEATURE_RSSI_MONITOR;
fset |= WIFI_FEATURE_TX_TRANSMIT_POWER;
fset |= WIFI_FEATURE_SET_TX_POWER_LIMIT;
fset |= WIFI_FEATURE_CONFIG_NDO;
if (hdd_link_layer_stats_supported())
fset |= WIFI_FEATURE_LINK_LAYER_STATS;
if (hdd_roaming_supported(hdd_ctx))
fset |= WIFI_FEATURE_CONTROL_ROAMING;
if (hdd_scan_random_mac_addr_supported())
fset |= WIFI_FEATURE_SCAN_RAND;
if (hdd_is_wlm_latency_manager_supported(hdd_ctx))
fset |= WIFI_FEATURE_SET_LATENCY_MODE;
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) +
NLMSG_HDRLEN);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -EINVAL;
}
hdd_debug("Supported Features : 0x%x", fset);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_SET, fset)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
ret = cfg80211_vendor_cmd_reply(skb);
return ret;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_supported_features() - get supported features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_supported_features(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Set the MAC address that is to be used for scanning.
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct scan_mac_oui scan_mac_oui = { {0} };
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1];
QDF_STATUS status;
int ret, len;
struct net_device *ndev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
mac_handle_t mac_handle;
bool mac_spoofing_enabled;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc);
if (!mac_spoofing_enabled) {
hdd_debug("MAC address spoofing is not enabled");
return -ENOTSUPP;
}
/*
* audit note: it is ok to pass a NULL policy here since only
* one attribute is parsed and it is explicitly validated
*/
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX,
data, data_len, NULL)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) {
hdd_err("attr mac oui failed");
return -EINVAL;
}
len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]);
if (len != sizeof(scan_mac_oui.oui)) {
hdd_err("attr mac oui invalid size %d expected %zu",
len, sizeof(scan_mac_oui.oui));
return -EINVAL;
}
nla_memcpy(scan_mac_oui.oui,
tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI],
sizeof(scan_mac_oui.oui));
/* populate rest of scan_mac_oui for mac addr randomization */
scan_mac_oui.vdev_id = adapter->vdev_id;
scan_mac_oui.enb_probe_req_sno_randomization = true;
hdd_debug("Oui (%02x:%02x:%02x), vdev_id = %d",
scan_mac_oui.oui[0], scan_mac_oui.oui[1],
scan_mac_oui.oui[2], scan_mac_oui.vdev_id);
hdd_update_ie_whitelist_attr(&scan_mac_oui.ie_whitelist, hdd_ctx);
mac_handle = hdd_ctx->mac_handle;
status = sme_set_scanning_mac_oui(mac_handle, &scan_mac_oui);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("sme_set_scanning_mac_oui failed(err=%d)", status);
return qdf_status_to_os_return(status);
}
/**
* wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Set the MAC address that is to be used for scanning. This is an
* SSR-protecting wrapper function.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_set_scanning_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_scanning_mac_oui(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_cfg80211_set_feature() - Set the bitmask for supported features
* @feature_flags: pointer to the byte array of features.
* @feature: Feature to be turned ON in the byte array.
*
* Return: None
*
* This is called to turn ON or SET the feature flag for the requested feature.
**/
#define NUM_BITS_IN_BYTE 8
static void wlan_hdd_cfg80211_set_feature(uint8_t *feature_flags,
uint8_t feature)
{
uint32_t index;
uint8_t bit_mask;
index = feature / NUM_BITS_IN_BYTE;
bit_mask = 1 << (feature % NUM_BITS_IN_BYTE);
feature_flags[index] |= bit_mask;
}
/**
* __wlan_hdd_cfg80211_get_features() - Get the Driver Supported features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send supported feature set to
* supplicant upon a request/query from the supplicant.
*
* Return: Return the Success or Failure code.
**/
#define MAX_CONCURRENT_CHAN_ON_24G 2
#define MAX_CONCURRENT_CHAN_ON_5G 2
static int
__wlan_hdd_cfg80211_get_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sk_buff *skb = NULL;
uint32_t dbs_capability = 0;
bool one_by_one_dbs, two_by_two_dbs;
bool value, twt_req, twt_res;
QDF_STATUS ret = QDF_STATUS_E_FAILURE;
QDF_STATUS status;
int ret_val;
uint8_t feature_flags[(NUM_QCA_WLAN_VENDOR_FEATURES + 7) / 8] = {0};
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
hdd_enter_dev(wdev->netdev);
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (roaming_offload_enabled(hdd_ctx)) {
hdd_debug("Key Mgmt Offload is supported");
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD);
}
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY);
if (policy_mgr_is_scan_simultaneous_capable(hdd_ctx->psoc))
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS);
if (wma_is_p2p_lo_capable())
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD);
value = 0;
status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc, &value);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not get OCE STA enable info");
if (value)
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_OCE_STA);
value = 0;
status = ucfg_mlme_get_oce_sap_enabled_info(hdd_ctx->psoc, &value);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not get OCE SAP enable info");
if (value)
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON);
ucfg_mlme_get_twt_requestor(hdd_ctx->psoc, &twt_req);
ucfg_mlme_get_twt_responder(hdd_ctx->psoc, &twt_res);
if (twt_req || twt_res)
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_TWT);
/* Check the kernel version for upstream commit aced43ce780dc5 that
* has support for processing user cell_base hints when wiphy is
* self managed or check the backport flag for the same.
*/
#if defined CFG80211_USER_HINT_CELL_BASE_SELF_MANAGED || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0))
wlan_hdd_cfg80211_set_feature(feature_flags,
QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY);
#endif
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(feature_flags) +
NLMSG_HDRLEN);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS,
sizeof(feature_flags), feature_flags))
goto nla_put_failure;
ret = policy_mgr_get_dbs_hw_modes(hdd_ctx->psoc,
&one_by_one_dbs, &two_by_two_dbs);
if (QDF_STATUS_SUCCESS == ret) {
if (one_by_one_dbs)
dbs_capability = DRV_DBS_CAPABILITY_1X1;
if (two_by_two_dbs)
dbs_capability = DRV_DBS_CAPABILITY_2X2;
if (!one_by_one_dbs && !two_by_two_dbs)
dbs_capability = DRV_DBS_CAPABILITY_DISABLED;
} else {
hdd_err("wma_get_dbs_hw_mode failed");
dbs_capability = DRV_DBS_CAPABILITY_DISABLED;
}
hdd_debug("dbs_capability is %d", dbs_capability);
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA,
dbs_capability))
goto nla_put_failure;
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND,
MAX_CONCURRENT_CHAN_ON_24G))
goto nla_put_failure;
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND,
MAX_CONCURRENT_CHAN_ON_5G))
goto nla_put_failure;
return cfg80211_vendor_cmd_reply(skb);
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_features() - Get the Driver Supported features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send supported feature set to
* supplicant upon a request/query from the supplicant.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_features(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#define PARAM_NUM_NW \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS
#define PARAM_SET_BSSID \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID
#define PARAM_SSID_LIST QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST
#define PARAM_LIST_SSID QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID
#define MAX_ROAMING_PARAM \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX
#define PARAM_NUM_BSSID \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID
#define PARAM_BSSID_PREFS \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS
#define PARAM_ROAM_BSSID \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID
#define PARAM_RSSI_MODIFIER \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER
#define PARAMS_NUM_BSSID \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID
#define PARAM_BSSID_PARAMS \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS
#define PARAM_A_BAND_BOOST_THLD \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD
#define PARAM_A_BAND_PELT_THLD \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD
#define PARAM_A_BAND_BOOST_FACTOR \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR
#define PARAM_A_BAND_PELT_FACTOR \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR
#define PARAM_A_BAND_MAX_BOOST \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST
#define PARAM_ROAM_HISTERESYS \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS
#define PARAM_RSSI_TRIGGER \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER
#define PARAM_ROAM_ENABLE \
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE
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},
[PARAM_NUM_NW] = {.type = NLA_U32},
[PARAM_A_BAND_BOOST_FACTOR] = {.type = NLA_U32},
[PARAM_A_BAND_PELT_FACTOR] = {.type = NLA_U32},
[PARAM_A_BAND_MAX_BOOST] = {.type = NLA_U32},
[PARAM_ROAM_HISTERESYS] = {.type = NLA_S32},
[PARAM_A_BAND_BOOST_THLD] = {.type = NLA_S32},
[PARAM_A_BAND_PELT_THLD] = {.type = NLA_S32},
[PARAM_RSSI_TRIGGER] = {.type = NLA_U32},
[PARAM_ROAM_ENABLE] = { .type = NLA_S32},
[PARAM_NUM_BSSID] = {.type = NLA_U32},
[PARAM_RSSI_MODIFIER] = {.type = NLA_U32},
[PARAMS_NUM_BSSID] = {.type = NLA_U32},
[PARAM_ROAM_BSSID] = {.type = NLA_UNSPEC, .len = QDF_MAC_ADDR_SIZE},
[PARAM_SET_BSSID] = {.type = NLA_UNSPEC, .len = QDF_MAC_ADDR_SIZE},
};
/**
* hdd_set_white_list() - parse white list
* @hdd_ctx: HDD context
* @roam_params: roam params
* @tb: list of attributes
* @vdev_id: vdev id
*
* Return: 0 on success; error number on failure
*/
static int hdd_set_white_list(struct hdd_context *hdd_ctx,
struct roam_ext_params *roam_params,
struct nlattr **tb, uint8_t vdev_id)
{
int rem, i;
uint32_t buf_len = 0, count;
struct nlattr *tb2[MAX_ROAMING_PARAM + 1];
struct nlattr *curr_attr = NULL;
mac_handle_t mac_handle;
i = 0;
if (tb[PARAM_NUM_NW]) {
count = nla_get_u32(tb[PARAM_NUM_NW]);
} else {
hdd_err("Number of networks is not provided");
goto fail;
}
if (count && tb[PARAM_SSID_LIST]) {
nla_for_each_nested(curr_attr,
tb[PARAM_SSID_LIST], rem) {
if (wlan_cfg80211_nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX,
nla_data(curr_attr),
nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hdd_err("nla_parse failed");
goto fail;
}
/* Parse and Fetch allowed SSID list*/
if (!tb2[PARAM_LIST_SSID]) {
hdd_err("attr allowed ssid failed");
goto fail;
}
buf_len = nla_len(tb2[PARAM_LIST_SSID]);
/*
* Upper Layers include a null termination
* character. Check for the actual permissible
* length of SSID and also ensure not to copy
* the NULL termination character to the driver
* buffer.
*/
if (buf_len && (i < MAX_SSID_ALLOWED_LIST) &&
((buf_len - 1) <= WLAN_SSID_MAX_LEN)) {
nla_memcpy(roam_params->ssid_allowed_list[i].ssId,
tb2[PARAM_LIST_SSID], buf_len - 1);
roam_params->ssid_allowed_list[i].length = buf_len - 1;
hdd_debug("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 {
hdd_err("Invalid buffer length");
}
}
}
if (i != count) {
hdd_err("Invalid number of SSIDs i = %d, count = %d", i, count);
goto fail;
}
roam_params->num_ssid_allowed_list = i;
hdd_debug("Num of Allowed SSID %d", roam_params->num_ssid_allowed_list);
mac_handle = hdd_ctx->mac_handle;
sme_update_roam_params(mac_handle, vdev_id,
roam_params, REASON_ROAM_SET_SSID_ALLOWED);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_set_bssid_prefs() - parse set bssid prefs
* @hdd_ctx: HDD context
* @roam_params: roam params
* @tb: list of attributes
* @vdev_id: vdev id
*
* Return: 0 on success; error number on failure
*/
static int hdd_set_bssid_prefs(struct hdd_context *hdd_ctx,
struct roam_ext_params *roam_params,
struct nlattr **tb, uint8_t vdev_id)
{
int rem, i;
uint32_t count;
struct nlattr *tb2[MAX_ROAMING_PARAM + 1];
struct nlattr *curr_attr = NULL;
mac_handle_t mac_handle;
/* Parse and fetch number of preferred BSSID */
if (!tb[PARAM_NUM_BSSID]) {
hdd_err("attr num of preferred bssid failed");
goto fail;
}
count = nla_get_u32(tb[PARAM_NUM_BSSID]);
if (count > MAX_BSSID_FAVORED) {
hdd_err("Preferred BSSID count %u exceeds max %u",
count, MAX_BSSID_FAVORED);
goto fail;
}
hdd_debug("Num of Preferred BSSID (%d)", count);
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS]) {
hdd_err("attr Preferred BSSID failed");
goto fail;
}
i = 0;
nla_for_each_nested(curr_attr,
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS],
rem) {
if (i == count) {
hdd_warn("Ignoring excess Preferred BSSID");
break;
}
if (wlan_cfg80211_nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
nla_data(curr_attr),
nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hdd_err("nla_parse failed");
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[PARAM_ROAM_BSSID]) {
hdd_err("attr mac address failed");
goto fail;
}
nla_memcpy(roam_params->bssid_favored[i].bytes,
tb2[PARAM_ROAM_BSSID],
QDF_MAC_ADDR_SIZE);
hdd_debug(QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(roam_params->bssid_favored[i].bytes));
/* Parse and fetch preference factor*/
if (!tb2[PARAM_RSSI_MODIFIER]) {
hdd_err("BSSID Preference score failed");
goto fail;
}
roam_params->bssid_favored_factor[i] = nla_get_u32(
tb2[PARAM_RSSI_MODIFIER]);
hdd_debug("BSSID Preference score (%d)",
roam_params->bssid_favored_factor[i]);
i++;
}
if (i < count)
hdd_warn("Num Preferred BSSID %u less than expected %u",
i, count);
roam_params->num_bssid_favored = i;
mac_handle = hdd_ctx->mac_handle;
sme_update_roam_params(mac_handle, vdev_id,
roam_params, REASON_ROAM_SET_FAVORED_BSSID);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_set_blacklist_bssid() - parse set blacklist bssid
* @hdd_ctx: HDD context
* @roam_params: roam params
* @tb: list of attributes
* @vdev_id: vdev id
*
* Return: 0 on success; error number on failure
*/
static int hdd_set_blacklist_bssid(struct hdd_context *hdd_ctx,
struct roam_ext_params *roam_params,
struct nlattr **tb,
uint8_t vdev_id)
{
int rem, i;
uint32_t count;
struct nlattr *tb2[MAX_ROAMING_PARAM + 1];
struct nlattr *curr_attr = NULL;
mac_handle_t mac_handle;
/* Parse and fetch number of blacklist BSSID */
if (!tb[PARAMS_NUM_BSSID]) {
hdd_err("attr num of blacklist bssid failed");
goto fail;
}
count = nla_get_u32(tb[PARAMS_NUM_BSSID]);
if (count > MAX_BSSID_AVOID_LIST) {
hdd_err("Blacklist BSSID count %u exceeds max %u",
count, MAX_BSSID_AVOID_LIST);
goto fail;
}
hdd_debug("Num of blacklist BSSID (%d)", count);
i = 0;
if (count && tb[PARAM_BSSID_PARAMS]) {
nla_for_each_nested(curr_attr,
tb[PARAM_BSSID_PARAMS],
rem) {
if (i == count) {
hdd_warn("Ignoring excess Blacklist BSSID");
break;
}
if (wlan_cfg80211_nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
nla_data(curr_attr),
nla_len(curr_attr),
wlan_hdd_set_roam_param_policy)) {
hdd_err("nla_parse failed");
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[PARAM_SET_BSSID]) {
hdd_err("attr blacklist addr failed");
goto fail;
}
nla_memcpy(roam_params->bssid_avoid_list[i].bytes,
tb2[PARAM_SET_BSSID], QDF_MAC_ADDR_SIZE);
hdd_debug(QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(roam_params->bssid_avoid_list[i].bytes));
i++;
}
}
if (i < count)
hdd_warn("Num Blacklist BSSID %u less than expected %u",
i, count);
roam_params->num_bssid_avoid_list = i;
mac_handle = hdd_ctx->mac_handle;
sme_update_roam_params(mac_handle, vdev_id,
roam_params, REASON_ROAM_SET_BLACKLIST_BSSID);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_set_ext_roam_params() - parse ext roam params
* @hdd_ctx: HDD context
* @roam_params: roam params
* @tb: list of attributes
* @vdev_id: vdev id
*
* Return: 0 on success; error number on failure
*/
static int hdd_set_ext_roam_params(struct hdd_context *hdd_ctx,
const void *data, int data_len,
uint8_t vdev_id,
struct roam_ext_params *roam_params)
{
uint32_t cmd_type, req_id;
struct nlattr *tb[MAX_ROAMING_PARAM + 1];
int ret;
mac_handle_t mac_handle;
if (wlan_cfg80211_nla_parse(tb, MAX_ROAMING_PARAM, data, data_len,
wlan_hdd_set_roam_param_policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
/* Parse and fetch Command Type */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]) {
hdd_err("roam cmd type failed");
goto fail;
}
cmd_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD]);
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]) {
hdd_err("attr request id failed");
goto fail;
}
mac_handle = hdd_ctx->mac_handle;
req_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID]);
hdd_debug("Req Id: %u Cmd Type: %u", req_id, cmd_type);
switch (cmd_type) {
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST:
ret = hdd_set_white_list(hdd_ctx, roam_params, tb, vdev_id);
if (ret)
goto fail;
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_EXTSCAN_ROAM_PARAMS:
/* Parse and fetch 5G Boost Threshold */
if (!tb[PARAM_A_BAND_BOOST_THLD]) {
hdd_err("5G boost threshold failed");
goto fail;
}
roam_params->raise_rssi_thresh_5g = nla_get_s32(
tb[PARAM_A_BAND_BOOST_THLD]);
hdd_debug("5G Boost Threshold (%d)",
roam_params->raise_rssi_thresh_5g);
/* Parse and fetch 5G Penalty Threshold */
if (!tb[PARAM_A_BAND_PELT_THLD]) {
hdd_err("5G penalty threshold failed");
goto fail;
}
roam_params->drop_rssi_thresh_5g = nla_get_s32(
tb[PARAM_A_BAND_PELT_THLD]);
hdd_debug("5G Penalty Threshold (%d)",
roam_params->drop_rssi_thresh_5g);
/* Parse and fetch 5G Boost Factor */
if (!tb[PARAM_A_BAND_BOOST_FACTOR]) {
hdd_err("5G boost Factor failed");
goto fail;
}
roam_params->raise_factor_5g = nla_get_u32(
tb[PARAM_A_BAND_BOOST_FACTOR]);
hdd_debug("5G Boost Factor (%d)",
roam_params->raise_factor_5g);
/* Parse and fetch 5G Penalty factor */
if (!tb[PARAM_A_BAND_PELT_FACTOR]) {
hdd_err("5G Penalty Factor failed");
goto fail;
}
roam_params->drop_factor_5g = nla_get_u32(
tb[PARAM_A_BAND_PELT_FACTOR]);
hdd_debug("5G Penalty factor (%d)",
roam_params->drop_factor_5g);
/* Parse and fetch 5G Max Boost */
if (!tb[PARAM_A_BAND_MAX_BOOST]) {
hdd_err("5G Max Boost failed");
goto fail;
}
roam_params->max_raise_rssi_5g = nla_get_u32(
tb[PARAM_A_BAND_MAX_BOOST]);
hdd_debug("5G Max Boost (%d)",
roam_params->max_raise_rssi_5g);
/* Parse and fetch Rssi Diff */
if (!tb[PARAM_ROAM_HISTERESYS]) {
hdd_err("Rssi Diff failed");
goto fail;
}
roam_params->rssi_diff = nla_get_s32(
tb[PARAM_ROAM_HISTERESYS]);
hdd_debug("RSSI Diff (%d)",
roam_params->rssi_diff);
/* Parse and fetch Alert Rssi Threshold */
if (!tb[PARAM_RSSI_TRIGGER]) {
hdd_err("Alert Rssi Threshold failed");
goto fail;
}
roam_params->alert_rssi_threshold = nla_get_u32(
tb[PARAM_RSSI_TRIGGER]);
hdd_debug("Alert RSSI Threshold (%d)",
roam_params->alert_rssi_threshold);
sme_update_roam_params(mac_handle, vdev_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[PARAM_ROAM_ENABLE]) {
hdd_err("Activate Good Rssi Roam failed");
goto fail;
}
roam_params->good_rssi_roam = nla_get_s32(
tb[PARAM_ROAM_ENABLE]);
hdd_debug("Activate Good Rssi Roam (%d)",
roam_params->good_rssi_roam);
sme_update_roam_params(mac_handle, vdev_id,
roam_params,
REASON_ROAM_GOOD_RSSI_CHANGED);
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS:
ret = hdd_set_bssid_prefs(hdd_ctx, roam_params, tb, vdev_id);
if (ret)
goto fail;
break;
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID:
ret = hdd_set_blacklist_bssid(hdd_ctx, roam_params,
tb, vdev_id);
if (ret)
goto fail;
break;
}
return 0;
fail:
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_set_ext_roam_params() - Settings for roaming parameters
* @wiphy: The wiphy structure
* @wdev: The wireless device
* @data: Data passed by framework
* @data_len: Parameters to be configured passed as data
*
* The roaming related parameters are configured by the framework
* using this interface.
*
* Return: Return either success or failure code.
*/
static int
__wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct roam_ext_params *roam_params = NULL;
int ret;
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) {
hdd_err("Driver Modules are closed");
return -EINVAL;
}
roam_params = qdf_mem_malloc(sizeof(*roam_params));
if (!roam_params)
return -ENOMEM;
ret = hdd_set_ext_roam_params(hdd_ctx, data, data_len,
adapter->vdev_id, roam_params);
if (ret)
goto fail;
if (roam_params)
qdf_mem_free(roam_params);
return 0;
fail:
if (roam_params)
qdf_mem_free(roam_params);
return ret;
}
#undef PARAM_NUM_NW
#undef PARAM_SET_BSSID
#undef PARAM_SSID_LIST
#undef PARAM_LIST_SSID
#undef MAX_ROAMING_PARAM
#undef PARAM_NUM_BSSID
#undef PARAM_BSSID_PREFS
#undef PARAM_ROAM_BSSID
#undef PARAM_RSSI_MODIFIER
#undef PARAMS_NUM_BSSID
#undef PARAM_BSSID_PARAMS
#undef PARAM_A_BAND_BOOST_THLD
#undef PARAM_A_BAND_PELT_THLD
#undef PARAM_A_BAND_BOOST_FACTOR
#undef PARAM_A_BAND_PELT_FACTOR
#undef PARAM_A_BAND_MAX_BOOST
#undef PARAM_ROAM_HISTERESYS
#undef PARAM_RSSI_TRIGGER
#undef PARAM_ROAM_ENABLE
/**
* wlan_hdd_cfg80211_set_ext_roam_params() - set ext scan roam params
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_set_ext_roam_params(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_ext_roam_params(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#define PWR_SAVE_FAIL_CMD_INDEX \
QCA_NL80211_VENDOR_SUBCMD_PWR_SAVE_FAIL_DETECTED_INDEX
void hdd_chip_pwr_save_fail_detected_cb(hdd_handle_t hdd_handle,
struct chip_pwr_save_fail_detected_params
*data)
{
struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle);
struct sk_buff *skb;
int flags = cds_get_gfp_flags();
hdd_enter();
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (!data) {
hdd_debug("data is null");
return;
}
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL, NLMSG_HDRLEN +
sizeof(data->failure_reason_code) +
NLMSG_HDRLEN, PWR_SAVE_FAIL_CMD_INDEX,
flags);
if (!skb) {
hdd_info("cfg80211_vendor_event_alloc failed");
return;
}
hdd_debug("failure reason code: %u", data->failure_reason_code);
if (nla_put_u32(skb,
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON,
data->failure_reason_code))
goto fail;
cfg80211_vendor_event(skb, flags);
hdd_exit();
return;
fail:
kfree_skb(skb);
}
#undef PWR_SAVE_FAIL_CMD_INDEX
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_check_dfs_channel_for_adapter() - check dfs channel in adapter
* @hdd_ctx: HDD context
* @device_mode: device mode
* Return: bool
*/
static bool wlan_hdd_check_dfs_channel_for_adapter(struct hdd_context *hdd_ctx,
enum QDF_OPMODE device_mode)
{
struct hdd_adapter *adapter;
struct hdd_ap_ctx *ap_ctx;
struct hdd_station_ctx *sta_ctx;
hdd_for_each_adapter(hdd_ctx, adapter) {
if ((device_mode == adapter->device_mode) &&
(device_mode == QDF_SAP_MODE)) {
ap_ctx =
WLAN_HDD_GET_AP_CTX_PTR(adapter);
/*
* if there is SAP already running on DFS channel,
* do not disable scan on dfs channels. Note that
* with SAP on DFS, there cannot be conurrency on
* single radio. But then we can have multiple
* radios !!
*/
if (CHANNEL_STATE_DFS == wlan_reg_get_channel_state(
hdd_ctx->pdev,
ap_ctx->operating_channel)) {
hdd_err("SAP running on DFS channel");
return true;
}
}
if ((device_mode == adapter->device_mode) &&
(device_mode == QDF_STA_MODE)) {
sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
/*
* if STA is already connected on DFS channel,
* do not disable scan on dfs channels
*/
if (hdd_conn_is_connected(sta_ctx) &&
(CHANNEL_STATE_DFS ==
wlan_reg_get_channel_state(hdd_ctx->pdev,
sta_ctx->conn_info.channel))) {
hdd_err("client connected on DFS channel");
return true;
}
}
}
return false;
}
/**
* wlan_hdd_enable_dfs_chan_scan() - disable/enable DFS channels
* @hdd_ctx: HDD context within host driver
* @enable_dfs_channels: If true, DFS channels can be used for scanning
*
* Loops through devices to see who is operating on DFS channels
* and then disables/enables DFS channels.
* Fails the disable request if any device is active on a DFS channel.
*
* Return: 0 or other error codes.
*/
int wlan_hdd_enable_dfs_chan_scan(struct hdd_context *hdd_ctx,
bool enable_dfs_channels)
{
QDF_STATUS status;
bool err;
mac_handle_t mac_handle;
bool enable_dfs_scan = true;
ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc,
&enable_dfs_scan);
if (enable_dfs_channels == enable_dfs_scan) {
hdd_debug("DFS channels are already %s",
enable_dfs_channels ? "enabled" : "disabled");
return 0;
}
if (!enable_dfs_channels) {
err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx,
QDF_STA_MODE);
if (err)
return -EOPNOTSUPP;
err = wlan_hdd_check_dfs_channel_for_adapter(hdd_ctx,
QDF_SAP_MODE);
if (err)
return -EOPNOTSUPP;
}
ucfg_scan_cfg_set_dfs_chan_scan_allowed(hdd_ctx->psoc,
enable_dfs_channels);
mac_handle = hdd_ctx->mac_handle;
status = sme_enable_dfs_chan_scan(mac_handle, enable_dfs_channels);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to set DFS channel scan flag to %d",
enable_dfs_channels);
return qdf_status_to_os_return(status);
}
hdd_abort_mac_scan_all_adapters(hdd_ctx);
/* pass dfs channel status to regulatory component */
status = ucfg_reg_enable_dfs_channels(hdd_ctx->pdev,
enable_dfs_channels);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to %s DFS channels",
enable_dfs_channels ? "enable" : "disable");
return qdf_status_to_os_return(status);
}
/**
* __wlan_hdd_cfg80211_disable_dfs_chan_scan() - DFS channel configuration
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
* Return: success(0) or reason code for failure
*/
static int __wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1];
int ret_val;
uint32_t no_dfs_flag = 0;
bool enable_dfs_scan = true;
hdd_enter_dev(dev);
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX,
data, data_len,
wlan_hdd_set_no_dfs_flag_config_policy)) {
hdd_err("invalid attr");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) {
hdd_err("attr dfs flag failed");
return -EINVAL;
}
no_dfs_flag = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]);
hdd_debug("DFS flag: %d", no_dfs_flag);
if (no_dfs_flag > 1) {
hdd_err("invalid value of dfs flag");
return -EINVAL;
}
ucfg_scan_cfg_get_dfs_chan_scan_allowed(hdd_ctx->psoc,
&enable_dfs_scan);
if (enable_dfs_scan) {
ret_val = wlan_hdd_enable_dfs_chan_scan(hdd_ctx, !no_dfs_flag);
} else {
if ((!no_dfs_flag) != enable_dfs_scan) {
hdd_err("DFS chan ini configured %d, no dfs flag: %d",
enable_dfs_scan,
no_dfs_flag);
return -EINVAL;
}
}
return ret_val;
}
/**
* wlan_hdd_cfg80211_disable_dfs_chan_scan () - DFS scan vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX. Validate it and
* call wlan_hdd_disable_dfs_chan_scan to send it to firmware.
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_disable_dfs_chan_scan(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_disable_dfs_chan_scan(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
static const struct nla_policy
wlan_hdd_wisa_cmd_policy[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_WISA_MODE] = {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and
* setup WISA Mode features.
*
* Return: Success(0) or reason code for failure
*/
static int __wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WISA_MAX + 1];
struct sir_wisa_params wisa;
int ret_val;
QDF_STATUS status;
bool wisa_mode;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
void *pdev = cds_get_context(QDF_MODULE_ID_TXRX);
mac_handle_t mac_handle;
struct cdp_vdev *txrx_vdev = NULL;
hdd_enter_dev(dev);
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
goto err;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WISA_MAX, data,
data_len, wlan_hdd_wisa_cmd_policy)) {
hdd_err("Invalid WISA cmd attributes");
ret_val = -EINVAL;
goto err;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]) {
hdd_err("Invalid WISA mode");
ret_val = -EINVAL;
goto err;
}
wisa_mode = !!nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_WISA_MODE]);
hdd_debug("WISA Mode: %d", wisa_mode);
wisa.mode = wisa_mode;
wisa.vdev_id = adapter->vdev_id;
mac_handle = hdd_ctx->mac_handle;
status = sme_set_wisa_params(mac_handle, &wisa);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Unable to set WISA mode: %d to FW", wisa_mode);
ret_val = -EINVAL;
}
if (QDF_IS_STATUS_SUCCESS(status) || !wisa_mode) {
txrx_vdev = cdp_get_vdev_from_vdev_id(soc,
(struct cdp_pdev *)pdev,
adapter->vdev_id);
if (!txrx_vdev)
ret_val = -EINVAL;
else
cdp_set_wisa_mode(soc, txrx_vdev, wisa_mode);
}
err:
hdd_exit();
return ret_val;
}
/**
* wlan_hdd_cfg80211_handle_wisa_cmd() - Handle WISA vendor cmd
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
*
* Handles QCA_WLAN_VENDOR_SUBCMD_WISA. Validate cmd attributes and
* setup WISA mode features.
*
* Return: Success(0) or reason code for failure
*/
static int wlan_hdd_cfg80211_handle_wisa_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_handle_wisa_cmd(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
struct hdd_station_info *hdd_get_stainfo(struct hdd_station_info *astainfo,
struct qdf_mac_addr mac_addr)
{
struct hdd_station_info *stainfo = NULL;
int i;
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
if (!qdf_mem_cmp(&astainfo[i].sta_mac,
&mac_addr,
QDF_MAC_ADDR_SIZE)) {
stainfo = &astainfo[i];
break;
}
}
return stainfo;
}
/*
* undef short names defined for get station command
* used by __wlan_hdd_cfg80211_get_station_cmd()
*/
#undef STATION_INVALID
#undef STATION_INFO
#undef STATION_ASSOC_FAIL_REASON
#undef STATION_REMOTE
#undef STATION_MAX
#undef LINK_INFO_STANDARD_NL80211_ATTR
#undef AP_INFO_STANDARD_NL80211_ATTR
#undef INFO_ROAM_COUNT
#undef INFO_AKM
#undef WLAN802_11_MODE
#undef AP_INFO_HS20_INDICATION
#undef HT_OPERATION
#undef VHT_OPERATION
#undef INFO_ASSOC_FAIL_REASON
#undef REMOTE_MAX_PHY_RATE
#undef REMOTE_TX_PACKETS
#undef REMOTE_TX_BYTES
#undef REMOTE_RX_PACKETS
#undef REMOTE_RX_BYTES
#undef REMOTE_LAST_TX_RATE
#undef REMOTE_LAST_RX_RATE
#undef REMOTE_WMM
#undef REMOTE_SUPPORTED_MODE
#undef REMOTE_AMPDU
#undef REMOTE_TX_STBC
#undef REMOTE_RX_STBC
#undef REMOTE_CH_WIDTH
#undef REMOTE_SGI_ENABLE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#undef REMOTE_PAD
#endif
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
/**
* __wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the Key data
* @data_len:Length of the data passed
*
* This is called when wlan driver needs to save the keys received via
* vendor specific command.
*
* Return: Return the Success or Failure code.
*/
static int __wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
uint8_t local_pmk[SIR_ROAM_SCAN_PSK_SIZE];
struct net_device *dev = wdev->netdev;
struct hdd_adapter *hdd_adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx;
int status;
struct pmkid_mode_bits pmkid_modes;
mac_handle_t mac_handle;
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if ((!data) || (data_len <= 0) ||
(data_len > SIR_ROAM_SCAN_PSK_SIZE)) {
hdd_err("Invalid data");
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(hdd_adapter);
if (!hdd_ctx) {
hdd_err("HDD context is null");
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
hdd_get_pmkid_modes(hdd_ctx, &pmkid_modes);
mac_handle = hdd_ctx->mac_handle;
sme_update_roam_key_mgmt_offload_enabled(mac_handle,
hdd_adapter->vdev_id,
true, &pmkid_modes);
qdf_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE);
qdf_mem_copy(local_pmk, data, data_len);
sme_roam_set_psk_pmk(mac_handle, hdd_adapter->vdev_id,
local_pmk, data_len);
qdf_mem_zero(&local_pmk, SIR_ROAM_SCAN_PSK_SIZE);
return 0;
}
/**
* wlan_hdd_cfg80211_keymgmt_set_key() - Store the Keys in the driver session
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the Key data
* @data_len:Length of the data passed
*
* This is called when wlan driver needs to save the keys received via
* vendor specific command.
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_keymgmt_set_key(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_keymgmt_set_key(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
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)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1];
tSirVersionString driver_version;
tSirVersionString firmware_version;
const char *hw_version;
uint32_t major_spid = 0, minor_spid = 0, siid = 0, crmid = 0;
uint32_t sub_id = 0;
int status;
struct sk_buff *reply_skb;
uint32_t skb_len = 0, count = 0;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
if (wlan_cfg80211_nla_parse(tb_vendor,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX,
data, data_len,
qca_wlan_vendor_get_wifi_info_policy)) {
hdd_err("WIFI_INFO_GET NL CMD parsing failed");
return -EINVAL;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
hdd_debug("Rcvd req for Driver version");
strlcpy(driver_version, QWLAN_VERSIONSTR,
sizeof(driver_version));
skb_len += strlen(driver_version) + 1;
count++;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
hdd_debug("Rcvd req for FW version");
hdd_get_fw_version(hdd_ctx, &major_spid, &minor_spid, &siid,
&crmid);
sub_id = (hdd_ctx->target_fw_vers_ext & 0xf0000000) >> 28;
hw_version = hdd_ctx->target_hw_name;
snprintf(firmware_version, sizeof(firmware_version),
"FW:%d.%d.%d.%d.%d HW:%s", major_spid, minor_spid,
siid, crmid, sub_id, hw_version);
skb_len += strlen(firmware_version) + 1;
count++;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) {
hdd_debug("Rcvd req for Radio index");
skb_len += sizeof(uint32_t);
count++;
}
if (count == 0) {
hdd_err("unknown attribute in get_wifi_info request");
return -EINVAL;
}
skb_len += (NLA_HDRLEN * count) + NLMSG_HDRLEN;
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
if (!reply_skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
if (nla_put_string(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION,
driver_version))
goto error_nla_fail;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
if (nla_put_string(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION,
firmware_version))
goto error_nla_fail;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX]) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX,
hdd_ctx->radio_index))
goto error_nla_fail;
}
return cfg80211_vendor_cmd_reply(reply_skb);
error_nla_fail:
hdd_err("nla put fail");
kfree_skb(reply_skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send wifi driver related info
* (driver/fw version) to the user space application upon request.
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_wifi_info(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int status;
uint32_t features;
struct sk_buff *reply_skb = NULL;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
features = 0;
features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED;
features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
features |= WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
features |= WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
features |= WIFI_LOGGER_PACKET_FATE_SUPPORTED;
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN);
if (!reply_skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
hdd_debug("Supported logger features: 0x%0x", features);
if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED,
features)) {
hdd_err("nla put fail");
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#ifdef WLAN_FEATURE_GTK_OFFLOAD
void wlan_hdd_save_gtk_offload_params(struct hdd_adapter *adapter,
uint8_t *kck_ptr, uint8_t kck_len,
uint8_t *kek_ptr, uint32_t kek_len,
uint8_t *replay_ctr, bool big_endian)
{
struct hdd_station_ctx *hdd_sta_ctx;
uint8_t *buf;
int i;
struct pmo_gtk_req *gtk_req = NULL;
struct wlan_objmgr_vdev *vdev;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
gtk_req = qdf_mem_malloc(sizeof(*gtk_req));
if (!gtk_req)
return;
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (kck_ptr) {
if (kck_len > sizeof(gtk_req->kck)) {
kck_len = sizeof(gtk_req->kck);
QDF_ASSERT(0);
}
qdf_mem_copy(gtk_req->kck, kck_ptr, kck_len);
}
if (kek_ptr) {
/* paranoia */
if (kek_len > sizeof(gtk_req->kek)) {
kek_len = sizeof(gtk_req->kek);
QDF_ASSERT(0);
}
qdf_mem_copy(gtk_req->kek, kek_ptr, kek_len);
}
qdf_copy_macaddr(&gtk_req->bssid, &hdd_sta_ctx->conn_info.bssid);
gtk_req->kek_len = kek_len;
gtk_req->is_fils_connection = hdd_is_fils_connection(adapter);
/* convert big to little endian since driver work on little endian */
buf = (uint8_t *)&gtk_req->replay_counter;
for (i = 0; i < 8; i++)
buf[7 - i] = replay_ctr[i];
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
goto end;
status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req);
hdd_objmgr_put_vdev(vdev);
if (status != QDF_STATUS_SUCCESS)
hdd_err("Failed to cache GTK Offload");
end:
qdf_mem_free(gtk_req);
}
#endif
#if defined(WLAN_FEATURE_FILS_SK) && defined(WLAN_FEATURE_ROAM_OFFLOAD)
/**
* wlan_hdd_add_fils_params_roam_auth_event() - Adds FILS params in roam auth
* @skb: SK buffer
* @roam_info: Roam info
*
* API adds fils params[pmk, pmkid, next sequence number] to roam auth event
*
* Return: zero on success, error code on failure
*/
static int
wlan_hdd_add_fils_params_roam_auth_event(struct sk_buff *skb,
struct csr_roam_info *roam_info)
{
if (roam_info->pmk_len &&
nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK,
roam_info->pmk_len, roam_info->pmk)) {
hdd_err("pmk send fail");
return -EINVAL;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID,
PMKID_LEN, roam_info->pmkid)) {
hdd_err("pmkid send fail");
return -EINVAL;
}
hdd_debug("Update ERP Seq Num %d, Next ERP Seq Num %d",
roam_info->update_erp_next_seq_num,
roam_info->next_erp_seq_num);
if (roam_info->update_erp_next_seq_num &&
nla_put_u16(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM,
roam_info->next_erp_seq_num)) {
hdd_err("ERP seq num send fail");
return -EINVAL;
}
return 0;
}
#else
static inline int
wlan_hdd_add_fils_params_roam_auth_event(struct sk_buff *skb,
struct csr_roam_info *roam_info)
{
return 0;
}
#endif
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
/**
* wlan_hdd_send_roam_auth_event() - Send the roamed and authorized event
* @adapter: Pointer to adapter struct
* @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(struct hdd_adapter *adapter, uint8_t *bssid,
uint8_t *req_rsn_ie, uint32_t req_rsn_len, uint8_t *rsp_rsn_ie,
uint32_t rsp_rsn_len, struct csr_roam_info *roam_info_ptr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct sk_buff *skb = NULL;
eCsrAuthType auth_type;
uint32_t fils_params_len;
int status;
hdd_enter();
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (!roaming_offload_enabled(hdd_ctx) ||
!roam_info_ptr->roamSynchInProgress)
return 0;
/*
* PMK is sent from FW in Roam Synch Event for FILS Roaming.
* In that case, add three more NL attributes.ie. PMK, PMKID
* and ERP next sequence number. Add corresponding lengths
* with 3 extra NL message headers for each of the
* aforementioned params.
*/
fils_params_len = roam_info_ptr->pmk_len + PMKID_LEN +
sizeof(uint16_t) + (3 * NLMSG_HDRLEN);
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
&(adapter->wdev),
ETH_ALEN + req_rsn_len + rsp_rsn_len +
sizeof(uint8_t) + SIR_REPLAY_CTR_LEN +
SIR_KCK_KEY_LEN + roam_info_ptr->kek_len +
sizeof(uint8_t) + (8 * NLMSG_HDRLEN) +
fils_params_len,
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH_INDEX,
GFP_KERNEL);
if (!skb) {
hdd_err("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)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
if (roam_info_ptr->synchAuthStatus ==
CSR_ROAM_AUTH_STATUS_AUTHENTICATED) {
hdd_debug("Include Auth Params TLV's");
if (nla_put_u8(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, true)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
auth_type = roam_info_ptr->u.pConnectedProfile->AuthType;
/* if FT or CCKM connection: dont send replay counter */
if (auth_type != eCSR_AUTH_TYPE_FT_RSN &&
auth_type != eCSR_AUTH_TYPE_FT_RSN_PSK &&
auth_type != eCSR_AUTH_TYPE_FT_SAE &&
auth_type != eCSR_AUTH_TYPE_FT_SUITEB_EAP_SHA384 &&
auth_type != eCSR_AUTH_TYPE_CCKM_WPA &&
auth_type != eCSR_AUTH_TYPE_CCKM_RSN &&
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
SIR_REPLAY_CTR_LEN,
roam_info_ptr->replay_ctr)) {
hdd_err("non FT/non CCKM connection");
hdd_err("failed to send replay counter");
goto nla_put_failure;
}
if (roam_info_ptr->kek_len > SIR_KEK_KEY_LEN_FILS ||
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
roam_info_ptr->kck_len, roam_info_ptr->kck) ||
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
roam_info_ptr->kek_len, roam_info_ptr->kek)) {
hdd_err("nla put fail, kek_len %d",
roam_info_ptr->kek_len);
goto nla_put_failure;
}
status = wlan_hdd_add_fils_params_roam_auth_event(skb,
roam_info_ptr);
if (status)
goto nla_put_failure;
/*
* Save the gtk rekey parameters in HDD STA context. They will
* be used next time when host enables GTK offload and goes
* into power save state.
*/
wlan_hdd_save_gtk_offload_params(adapter, roam_info_ptr->kck,
roam_info_ptr->kck_len,
roam_info_ptr->kek,
roam_info_ptr->kek_len,
roam_info_ptr->replay_ctr,
true);
hdd_debug("roam_info_ptr->replay_ctr 0x%llx",
*((uint64_t *)roam_info_ptr->replay_ctr));
} else {
hdd_debug("No Auth Params TLV's");
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
false)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
}
hdd_debug("Auth Status = %d Subnet Change Status = %d",
roam_info_ptr->synchAuthStatus,
roam_info_ptr->subnet_change_status);
/*
* Add subnet change status if subnet has changed
* 0 = unchanged
* 1 = changed
* 2 = unknown
*/
if (roam_info_ptr->subnet_change_status) {
if (nla_put_u8(skb,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
roam_info_ptr->subnet_change_status)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
}
cfg80211_vendor_event(skb, GFP_KERNEL);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
#endif
#define ANT_DIV_SET_PERIOD(probe_period, stay_period) \
((1 << 26) | \
(((probe_period) & 0x1fff) << 13) | \
((stay_period) & 0x1fff))
#define ANT_DIV_SET_SNR_DIFF(snr_diff) \
((1 << 27) | \
((snr_diff) & 0x1fff))
#define ANT_DIV_SET_PROBE_DWELL_TIME(probe_dwell_time) \
((1 << 28) | \
((probe_dwell_time) & 0x1fff))
#define ANT_DIV_SET_WEIGHT(mgmt_snr_weight, data_snr_weight, ack_snr_weight) \
((1 << 29) | \
(((mgmt_snr_weight) & 0xff) << 16) | \
(((data_snr_weight) & 0xff) << 8) | \
((ack_snr_weight) & 0xff))
#define RX_REORDER_TIMEOUT_VOICE \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE
#define RX_REORDER_TIMEOUT_VIDEO \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO
#define RX_REORDER_TIMEOUT_BESTEFFORT \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT
#define RX_REORDER_TIMEOUT_BACKGROUND \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND
#define RX_BLOCKSIZE_PEER_MAC \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC
#define RX_BLOCKSIZE_WINLIMIT \
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT
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_IGNORE_ASSOC_DISALLOWED] = {
.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR] = {.type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY] = {
.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL] = {.type = NLA_U8},
[RX_REORDER_TIMEOUT_VOICE] = {.type = NLA_U32},
[RX_REORDER_TIMEOUT_VIDEO] = {.type = NLA_U32},
[RX_REORDER_TIMEOUT_BESTEFFORT] = {.type = NLA_U32},
[RX_REORDER_TIMEOUT_BACKGROUND] = {.type = NLA_U32},
[RX_BLOCKSIZE_PEER_MAC] = {
.type = NLA_UNSPEC,
.len = QDF_MAC_ADDR_SIZE},
[RX_BLOCKSIZE_WINLIMIT] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_LRO] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL] = {.type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_GTX] = {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST] = {
.type = NLA_BINARY,
.len = WLAN_MAX_IE_LEN + 2},
};
static const struct nla_policy qca_wlan_vendor_twt_add_dialog_policy[
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST] = {.type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER] = {.type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION] = {.type = NLA_FLAG },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA] = {
.type = NLA_U32 },
};
static const struct nla_policy
qca_wlan_vendor_attr_he_omi_tx_policy [QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE] = {.type = NLA_U8 },
};
static const struct nla_policy
wlan_hdd_wifi_test_config_policy[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE] = {
.type = NLA_U16},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG] = {
.type = NLA_FLAG},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX] = {
.type = NLA_NESTED},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS]
= {.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT] = {
.type = NLA_U8},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP] = {
.type = NLA_NESTED},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE] = {
.type = NLA_NESTED},
[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT] = {
.type = NLA_U8},
};
/**
* wlan_hdd_add_qcn_ie() - Add QCN IE to a given IE buffer
* @ie_data: IE buffer
* @ie_len: length of the @ie_data
*
* Return: QDF_STATUS
*/
static QDF_STATUS wlan_hdd_add_qcn_ie(uint8_t *ie_data, uint16_t *ie_len)
{
tDot11fIEQCN_IE qcn_ie;
uint8_t qcn_ie_hdr[QCN_IE_HDR_LEN]
= {IE_EID_VENDOR, DOT11F_IE_QCN_IE_MAX_LEN,
0x8C, 0xFD, 0xF0, 0x1};
if (((*ie_len) + QCN_IE_HDR_LEN +
QCN_IE_VERSION_SUBATTR_DATA_LEN) > MAX_DEFAULT_SCAN_IE_LEN) {
hdd_err("IE buffer not enough for QCN IE");
return QDF_STATUS_E_FAILURE;
}
/* Add QCN IE header */
qdf_mem_copy(ie_data + (*ie_len), qcn_ie_hdr, QCN_IE_HDR_LEN);
(*ie_len) += QCN_IE_HDR_LEN;
/* Retrieve Version sub-attribute data */
populate_dot11f_qcn_ie(&qcn_ie);
/* Add QCN IE data[version sub attribute] */
qdf_mem_copy(ie_data + (*ie_len), qcn_ie.version,
(QCN_IE_VERSION_SUBATTR_LEN));
(*ie_len) += (QCN_IE_VERSION_SUBATTR_LEN);
return QDF_STATUS_SUCCESS;
}
/**
* wlan_hdd_save_default_scan_ies() - API to store the default scan IEs
* @hdd_ctx: HDD context
* @adapter: Pointer to HDD adapter
* @ie_data: Pointer to Scan IEs buffer
* @ie_len: Length of Scan IEs
*
* This API is used to store the default scan ies received from
* supplicant. Also saves QCN IE if g_qcn_ie_support INI is enabled
*
* Return: 0 on success; error number otherwise
*/
static int wlan_hdd_save_default_scan_ies(struct hdd_context *hdd_ctx,
struct hdd_adapter *adapter,
uint8_t *ie_data, uint16_t ie_len)
{
struct hdd_scan_info *scan_info = &adapter->scan_info;
bool add_qcn_ie;
if (!scan_info)
return -EINVAL;
if (scan_info->default_scan_ies) {
qdf_mem_free(scan_info->default_scan_ies);
scan_info->default_scan_ies = NULL;
}
scan_info->default_scan_ies_len = ie_len;
ucfg_mlme_get_qcn_ie_support(hdd_ctx->psoc, &add_qcn_ie);
if (add_qcn_ie)
ie_len += (QCN_IE_HDR_LEN + QCN_IE_VERSION_SUBATTR_LEN);
scan_info->default_scan_ies = qdf_mem_malloc(ie_len);
if (!scan_info->default_scan_ies) {
scan_info->default_scan_ies_len = 0;
return -ENOMEM;
}
qdf_mem_copy(scan_info->default_scan_ies, ie_data,
scan_info->default_scan_ies_len);
/* Add QCN IE if g_qcn_ie_support INI is enabled */
if (add_qcn_ie)
wlan_hdd_add_qcn_ie(scan_info->default_scan_ies,
&(scan_info->default_scan_ies_len));
hdd_debug("Saved default scan IE:");
qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
(uint8_t *) scan_info->default_scan_ies,
scan_info->default_scan_ies_len);
return 0;
}
/**
* wlan_hdd_handle_restrict_offchan_config() -
* Handle wifi configuration attribute :
* QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL
* @adapter: Pointer to HDD adapter
* @restrict_offchan: Restrict offchannel setting done by
* application
*
* Return: 0 on success; error number otherwise
*/
static int wlan_hdd_handle_restrict_offchan_config(struct hdd_adapter *adapter,
u8 restrict_offchan)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
enum QDF_OPMODE dev_mode = adapter->device_mode;
struct wlan_objmgr_vdev *vdev;
int ret_val = 0;
if (!(dev_mode == QDF_SAP_MODE || dev_mode == QDF_P2P_GO_MODE)) {
hdd_err("Invalid interface type:%d", dev_mode);
return -EINVAL;
}
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
if (restrict_offchan == 1) {
enum policy_mgr_con_mode pmode =
policy_mgr_convert_device_mode_to_qdf_type(dev_mode);
int chan;
u32 vdev_id = wlan_vdev_get_id(vdev);
wlan_vdev_obj_lock(vdev);
wlan_vdev_mlme_cap_set(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN);
wlan_vdev_obj_unlock(vdev);
chan = policy_mgr_get_channel(hdd_ctx->psoc, pmode,
&vdev_id);
if (!chan ||
wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, chan)) {
hdd_err("unable to send avoid_freq");
ret_val = -EINVAL;
}
hdd_info("vdev %d mode %d dnbs enabled", vdev_id, dev_mode);
} else if (restrict_offchan == 0) {
wlan_vdev_obj_lock(vdev);
wlan_vdev_mlme_cap_clear(vdev, WLAN_VDEV_C_RESTRICT_OFFCHAN);
wlan_vdev_obj_unlock(vdev);
if (wlan_hdd_send_avoid_freq_for_dnbs(hdd_ctx, 0)) {
hdd_err("unable to clear avoid_freq");
ret_val = -EINVAL;
}
hdd_info("vdev mode %d dnbs disabled", dev_mode);
} else {
ret_val = -EINVAL;
hdd_err("Invalid RESTRICT_OFFCHAN setting");
}
hdd_objmgr_put_vdev(vdev);
return ret_val;
}
/**
* wlan_hdd_cfg80211_wifi_set_reorder_timeout() - set reorder timeout
* @adapter: Pointer to HDD adapter
* @tb: array of pointer to struct nlattr
*
* Return: 0 on success; error number otherwise
*/
static
int wlan_hdd_cfg80211_wifi_set_reorder_timeout(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int ret_val = 0;
QDF_STATUS qdf_status;
struct sir_set_rx_reorder_timeout_val reorder_timeout;
mac_handle_t mac_handle;
#define RX_TIMEOUT_VAL_MIN 10
#define RX_TIMEOUT_VAL_MAX 1000
if (tb[RX_REORDER_TIMEOUT_VOICE] ||
tb[RX_REORDER_TIMEOUT_VIDEO] ||
tb[RX_REORDER_TIMEOUT_BESTEFFORT] ||
tb[RX_REORDER_TIMEOUT_BACKGROUND]) {
/* if one is specified, all must be specified */
if (!tb[RX_REORDER_TIMEOUT_VOICE] ||
!tb[RX_REORDER_TIMEOUT_VIDEO] ||
!tb[RX_REORDER_TIMEOUT_BESTEFFORT] ||
!tb[RX_REORDER_TIMEOUT_BACKGROUND]) {
hdd_err("four AC timeout val are required MAC");
return -EINVAL;
}
reorder_timeout.rx_timeout_pri[0] = nla_get_u32(
tb[RX_REORDER_TIMEOUT_VOICE]);
reorder_timeout.rx_timeout_pri[1] = nla_get_u32(
tb[RX_REORDER_TIMEOUT_VIDEO]);
reorder_timeout.rx_timeout_pri[2] = nla_get_u32(
tb[RX_REORDER_TIMEOUT_BESTEFFORT]);
reorder_timeout.rx_timeout_pri[3] = nla_get_u32(
tb[RX_REORDER_TIMEOUT_BACKGROUND]);
/* timeout value is required to be in the rang 10 to 1000ms */
if (reorder_timeout.rx_timeout_pri[0] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[0] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[1] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[1] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[2] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[2] <= RX_TIMEOUT_VAL_MAX &&
reorder_timeout.rx_timeout_pri[3] >= RX_TIMEOUT_VAL_MIN &&
reorder_timeout.rx_timeout_pri[3] <= RX_TIMEOUT_VAL_MAX) {
mac_handle = hdd_ctx->mac_handle;
qdf_status = sme_set_reorder_timeout(mac_handle,
&reorder_timeout);
if (qdf_status != QDF_STATUS_SUCCESS) {
hdd_err("failed to set reorder timeout err %d",
qdf_status);
ret_val = -EPERM;
}
} else {
hdd_err("one of the timeout value is not in range");
ret_val = -EINVAL;
}
}
return ret_val;
}
/**
* wlan_hdd_cfg80211_wifi_set_rx_blocksize() - set rx blocksize
* @adapter: hdd adapter
* @tb: array of pointer to struct nlattr
*
* Return: 0 on success; error number otherwise
*/
static int wlan_hdd_cfg80211_wifi_set_rx_blocksize(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int ret_val = 0;
uint32_t set_value;
QDF_STATUS qdf_status;
struct sir_peer_set_rx_blocksize rx_blocksize;
mac_handle_t mac_handle;
#define WINDOW_SIZE_VAL_MIN 1
#define WINDOW_SIZE_VAL_MAX 64
if (tb[RX_BLOCKSIZE_PEER_MAC] ||
tb[RX_BLOCKSIZE_WINLIMIT]) {
/* if one is specified, both must be specified */
if (!tb[RX_BLOCKSIZE_PEER_MAC] ||
!tb[RX_BLOCKSIZE_WINLIMIT]) {
hdd_err("Both Peer MAC and windows limit required");
return -EINVAL;
}
memcpy(&rx_blocksize.peer_macaddr,
nla_data(tb[RX_BLOCKSIZE_PEER_MAC]),
sizeof(rx_blocksize.peer_macaddr)),
rx_blocksize.vdev_id = adapter->vdev_id;
set_value = nla_get_u32(tb[RX_BLOCKSIZE_WINLIMIT]);
/* maximum window size is 64 */
if (set_value >= WINDOW_SIZE_VAL_MIN &&
set_value <= WINDOW_SIZE_VAL_MAX) {
rx_blocksize.rx_block_ack_win_limit = set_value;
mac_handle = hdd_ctx->mac_handle;
qdf_status = sme_set_rx_set_blocksize(mac_handle,
&rx_blocksize);
if (qdf_status != QDF_STATUS_SUCCESS) {
hdd_err("failed to set aggr sizes err %d",
qdf_status);
ret_val = -EPERM;
}
} else {
hdd_err("window size val is not in range");
ret_val = -EINVAL;
}
}
return ret_val;
}
static int hdd_config_access_policy(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct nlattr *policy_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY];
struct nlattr *ielist_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST];
uint32_t access_policy;
uint8_t ie[WLAN_MAX_IE_LEN + 2];
QDF_STATUS status;
/* nothing to do if neither attribute is present */
if (!ielist_attr && !policy_attr)
return 0;
/* if one is present, both must be present */
if (!ielist_attr || !policy_attr) {
hdd_err("Missing attribute for %s",
policy_attr ?
"ACCESS_POLICY_IE_LIST" : "ACCESS_POLICY");
return -EINVAL;
}
/* validate the access policy */
access_policy = nla_get_u32(policy_attr);
switch (access_policy) {
case QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED:
case QCA_ACCESS_POLICY_DENY_UNLESS_LISTED:
/* valid */
break;
default:
hdd_err("Invalid value. access_policy %u", access_policy);
return -EINVAL;
}
/*
* ie length is validated by the nla_policy. need to make a
* copy since SME will always read WLAN_MAX_IE_LEN+2 bytes
*/
nla_memcpy(ie, ielist_attr, sizeof(ie));
hdd_debug("calling sme_update_access_policy_vendor_ie");
status = sme_update_access_policy_vendor_ie(hdd_ctx->mac_handle,
adapter->vdev_id,
ie, access_policy);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to set vendor ie and access policy, %d",
status);
return qdf_status_to_os_return(status);
}
static int hdd_config_mpdu_aggregation(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct nlattr *tx_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION];
struct nlattr *rx_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION];
uint8_t tx_size, rx_size;
struct sir_set_tx_rx_aggregation_size request;
QDF_STATUS status;
/* nothing to do if neither attribute is present */
if (!tx_attr && !rx_attr)
return 0;
/* if one is present, both must be present */
if (!tx_attr || !rx_attr) {
hdd_err("Missing attribute for %s",
tx_attr ? "RX" : "TX");
return -EINVAL;
}
tx_size = nla_get_u8(tx_attr);
rx_size = nla_get_u8(rx_attr);
if (!cfg_in_range(CFG_TX_AGGREGATION_SIZE, tx_size) ||
!cfg_in_range(CFG_RX_AGGREGATION_SIZE, rx_size)) {
hdd_err("TX %d RX %d MPDU aggr size not in range",
tx_size, rx_size);
return -EINVAL;
}
qdf_mem_zero(&request, sizeof(request));
request.tx_aggregation_size = tx_size;
request.rx_aggregation_size = rx_size;
request.vdev_id = adapter->vdev_id;
request.aggr_type = WMI_VDEV_CUSTOM_AGGR_TYPE_AMPDU;
status = wma_set_tx_rx_aggregation_size(&request);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("failed to set aggr sizes err %d", status);
return qdf_status_to_os_return(status);
}
static int hdd_config_ant_div_period(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct nlattr *probe_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD];
struct nlattr *stay_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD];
uint32_t probe_period, stay_period, ant_div_usrcfg;
int errno;
/* nothing to do if neither attribute is present */
if (!probe_attr && !stay_attr)
return 0;
/* if one is present, both must be present */
if (!probe_attr || !stay_attr) {
hdd_err("Missing attribute for %s",
probe_attr ? "STAY" : "PROBE");
return -EINVAL;
}
probe_period = nla_get_u32(probe_attr);
stay_period = nla_get_u32(stay_attr);
ant_div_usrcfg = ANT_DIV_SET_PERIOD(probe_period, stay_period);
hdd_debug("ant div set period: %x", ant_div_usrcfg);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (errno)
hdd_err("Failed to set ant div period, %d", errno);
return errno;
}
static int hdd_config_ant_div_snr_weight(struct hdd_adapter *adapter,
struct nlattr *tb[])
{
struct nlattr *mgmt_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT];
struct nlattr *data_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT];
struct nlattr *ack_attr =
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT];
uint32_t mgmt_snr, data_snr, ack_snr, ant_div_usrcfg;
int errno;
/* nothing to do if none of the attributes are present */
if (!mgmt_attr && !data_attr && !ack_attr)
return 0;
/* if one is present, all must be present */
if (!mgmt_attr || !data_attr || !ack_attr) {
hdd_err("Missing attribute");
return -EINVAL;
}
mgmt_snr = nla_get_u32(mgmt_attr);
data_snr = nla_get_u32(data_attr);
ack_snr = nla_get_u32(ack_attr);
ant_div_usrcfg = ANT_DIV_SET_WEIGHT(mgmt_snr, data_snr, ack_snr);
hdd_debug("ant div set weight: %x", ant_div_usrcfg);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (errno)
hdd_err("Failed to set ant div weight, %d", errno);
return errno;
}
static int hdd_config_fine_time_measurement(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint32_t user_capability;
uint32_t target_capability;
uint32_t final_capability;
QDF_STATUS status;
user_capability = nla_get_u32(attr);
target_capability = hdd_ctx->fine_time_meas_cap_target;
final_capability = user_capability & target_capability;
status = ucfg_mlme_set_fine_time_meas_cap(hdd_ctx->psoc,
final_capability);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Unable to set value, status %d", status);
return -EINVAL;
}
sme_update_fine_time_measurement_capab(hdd_ctx->mac_handle,
adapter->vdev_id,
final_capability);
ucfg_wifi_pos_set_ftm_cap(hdd_ctx->psoc, final_capability);
hdd_debug("user: 0x%x, target: 0x%x, final: 0x%x",
user_capability, target_capability, final_capability);
return 0;
}
static int hdd_config_modulated_dtim(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct wlan_objmgr_vdev *vdev;
uint32_t modulated_dtim;
QDF_STATUS status;
modulated_dtim = nla_get_u32(attr);
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
status = ucfg_pmo_config_modulated_dtim(vdev, modulated_dtim);
hdd_objmgr_put_vdev(vdev);
return qdf_status_to_os_return(status);
}
static int hdd_config_listen_interval(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct wlan_objmgr_vdev *vdev;
uint32_t listen_interval;
QDF_STATUS status;
listen_interval = nla_get_u32(attr);
if (listen_interval > cfg_max(CFG_PMO_ENABLE_DYNAMIC_DTIM)) {
hdd_err_rl("Invalid value for listen interval - %d",
listen_interval);
return -EINVAL;
}
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
status = ucfg_pmo_config_listen_interval(vdev, listen_interval);
hdd_objmgr_put_vdev(vdev);
return qdf_status_to_os_return(status);
}
static int hdd_config_lro(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t enable_flag;
enable_flag = nla_get_u8(attr);
return hdd_lro_set_reset(hdd_ctx, adapter, enable_flag);
}
static int hdd_config_scan_enable(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t enable_flag;
enable_flag = nla_get_u8(attr);
if (enable_flag)
ucfg_scan_psoc_set_enable(hdd_ctx->psoc,
REASON_USER_SPACE);
else
ucfg_scan_psoc_set_disable(hdd_ctx->psoc,
REASON_USER_SPACE);
return 0;
}
static int hdd_config_qpower(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t qpower;
qpower = nla_get_u8(attr);
return hdd_set_qpower_config(hdd_ctx, adapter, qpower);
}
static int hdd_config_stats_avg_factor(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint16_t stats_avg_factor;
QDF_STATUS status;
stats_avg_factor = nla_get_u16(attr);
status = sme_configure_stats_avg_factor(hdd_ctx->mac_handle,
adapter->vdev_id,
stats_avg_factor);
return qdf_status_to_os_return(status);
}
static int hdd_config_non_agg_retry(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t retry;
int param_id;
retry = nla_get_u8(attr);
retry = retry > CFG_NON_AGG_RETRY_MAX ?
CFG_NON_AGG_RETRY_MAX : retry;
param_id = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH;
return wma_cli_set_command(adapter->vdev_id, param_id,
retry, PDEV_CMD);
}
static int hdd_config_agg_retry(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t retry;
int param_id;
retry = nla_get_u8(attr);
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;
param_id = WMI_PDEV_PARAM_AGG_SW_RETRY_TH;
return wma_cli_set_command(adapter->vdev_id, param_id,
retry, PDEV_CMD);
}
static int hdd_config_mgmt_retry(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t retry;
int param_id;
retry = nla_get_u8(attr);
retry = retry > CFG_MGMT_RETRY_MAX ?
CFG_MGMT_RETRY_MAX : retry;
param_id = WMI_PDEV_PARAM_MGMT_RETRY_LIMIT;
return wma_cli_set_command(adapter->vdev_id, param_id,
retry, PDEV_CMD);
}
static int hdd_config_ctrl_retry(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t retry;
int param_id;
retry = nla_get_u8(attr);
retry = retry > CFG_CTRL_RETRY_MAX ?
CFG_CTRL_RETRY_MAX : retry;
param_id = WMI_PDEV_PARAM_CTRL_RETRY_LIMIT;
return wma_cli_set_command(adapter->vdev_id, param_id,
retry, PDEV_CMD);
}
static int hdd_config_propagation_delay(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t delay;
uint32_t abs_delay;
int param_id;
delay = nla_get_u8(attr);
delay = delay > CFG_PROPAGATION_DELAY_MAX ?
CFG_PROPAGATION_DELAY_MAX : delay;
abs_delay = delay + CFG_PROPAGATION_DELAY_BASE;
param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY;
return wma_cli_set_command(adapter->vdev_id, param_id,
abs_delay, PDEV_CMD);
}
static int hdd_config_propagation_abs_delay(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t abs_delay;
int param_id;
abs_delay = nla_get_u32(attr);
param_id = WMI_PDEV_PARAM_PROPAGATION_DELAY;
return wma_cli_set_command(adapter->vdev_id, param_id,
abs_delay, PDEV_CMD);
}
static int hdd_config_tx_fail_count(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint32_t tx_fail_count;
QDF_STATUS status;
tx_fail_count = nla_get_u32(attr);
if (!tx_fail_count)
return 0;
status = sme_update_tx_fail_cnt_threshold(hdd_ctx->mac_handle,
adapter->vdev_id,
tx_fail_count);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("sme_update_tx_fail_cnt_threshold (err=%d)",
status);
return qdf_status_to_os_return(status);
}
static int hdd_config_channel_avoidance_ind(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t set_value;
set_value = nla_get_u8(attr);
hdd_debug("set_value: %d", set_value);
return hdd_enable_disable_ca_event(hdd_ctx, set_value);
}
static int hdd_config_guard_time(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint32_t guard_time;
QDF_STATUS status;
guard_time = nla_get_u32(attr);
status = sme_configure_guard_time(hdd_ctx->mac_handle,
adapter->vdev_id,
guard_time);
return qdf_status_to_os_return(status);
}
static int hdd_config_scan_default_ies(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t *scan_ie;
uint16_t scan_ie_len;
QDF_STATUS status;
mac_handle_t mac_handle;
scan_ie_len = nla_len(attr);
hdd_debug("IE len %d session %d device mode %d",
scan_ie_len, adapter->vdev_id, adapter->device_mode);
if (!scan_ie_len) {
hdd_err("zero-length IE prohibited");
return -EINVAL;
}
if (scan_ie_len > MAX_DEFAULT_SCAN_IE_LEN) {
hdd_err("IE length %d exceeds max of %d",
scan_ie_len, MAX_DEFAULT_SCAN_IE_LEN);
return -EINVAL;
}
scan_ie = nla_data(attr);
if (!wlan_is_ie_valid(scan_ie, scan_ie_len)) {
hdd_err("Invalid default scan IEs");
return -EINVAL;
}
if (wlan_hdd_save_default_scan_ies(hdd_ctx, adapter,
scan_ie, scan_ie_len))
hdd_err("Failed to save default scan IEs");
if (adapter->device_mode == QDF_STA_MODE) {
mac_handle = hdd_ctx->mac_handle;
status = sme_set_default_scan_ie(mac_handle,
adapter->vdev_id, scan_ie,
scan_ie_len);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("failed to set default scan IEs in sme: %d",
status);
return -EPERM;
}
}
return 0;
}
static int hdd_config_ant_div_ena(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t antdiv_enable;
int errno;
antdiv_enable = nla_get_u32(attr);
hdd_debug("antdiv_enable: %d", antdiv_enable);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ENA_ANT_DIV,
antdiv_enable, PDEV_CMD);
if (errno)
hdd_err("Failed to set antdiv_enable, %d", errno);
return errno;
}
static int hdd_config_ant_div_snr_diff(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t ant_div_snr_diff;
uint32_t ant_div_usrcfg;
int errno;
ant_div_snr_diff = nla_get_u32(attr);
hdd_debug("snr diff: %x", ant_div_snr_diff);
ant_div_usrcfg = ANT_DIV_SET_SNR_DIFF(ant_div_snr_diff);
hdd_debug("usrcfg: %x", ant_div_usrcfg);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (errno)
hdd_err("Failed to set snr diff, %d", errno);
return errno;
}
static int hdd_config_ant_div_probe_dwell_time(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t dwell_time;
uint32_t ant_div_usrcfg;
int errno;
dwell_time = nla_get_u32(attr);
hdd_debug("dwell time: %x", dwell_time);
ant_div_usrcfg = ANT_DIV_SET_PROBE_DWELL_TIME(dwell_time);
hdd_debug("usrcfg: %x", ant_div_usrcfg);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_USRCFG,
ant_div_usrcfg, PDEV_CMD);
if (errno)
hdd_err("Failed to set probe dwell time, %d", errno);
return errno;
}
static int hdd_config_ant_div_chain(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t antdiv_chain;
int errno;
antdiv_chain = nla_get_u32(attr);
hdd_debug("antdiv_chain: %d", antdiv_chain);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_FORCE_CHAIN_ANT,
antdiv_chain, PDEV_CMD);
if (errno)
hdd_err("Failed to set chain, %d", errno);
return errno;
}
static int hdd_config_ant_div_selftest(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t antdiv_selftest;
int errno;
antdiv_selftest = nla_get_u32(attr);
hdd_debug("antdiv_selftest: %d", antdiv_selftest);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_SELFTEST,
antdiv_selftest, PDEV_CMD);
if (errno)
hdd_err("Failed to set selftest, %d", errno);
return errno;
}
static int hdd_config_ant_div_selftest_intvl(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint32_t antdiv_selftest_intvl;
int errno;
antdiv_selftest_intvl = nla_get_u32(attr);
hdd_debug("antdiv_selftest_intvl: %d", antdiv_selftest_intvl);
errno = wma_cli_set_command(adapter->vdev_id,
WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL,
antdiv_selftest_intvl, PDEV_CMD);
if (errno)
hdd_err("Failed to set selftest interval, %d", errno);
return errno;
}
static int hdd_config_ignore_assoc_disallowed(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t ignore_assoc_disallowed;
ignore_assoc_disallowed = nla_get_u8(attr);
hdd_debug("%u", ignore_assoc_disallowed);
if ((ignore_assoc_disallowed < QCA_IGNORE_ASSOC_DISALLOWED_DISABLE) ||
(ignore_assoc_disallowed > QCA_IGNORE_ASSOC_DISALLOWED_ENABLE))
return -EINVAL;
sme_update_session_param(hdd_ctx->mac_handle,
adapter->vdev_id,
SIR_PARAM_IGNORE_ASSOC_DISALLOWED,
ignore_assoc_disallowed);
return 0;
}
static int hdd_config_restrict_offchannel(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t restrict_offchan;
restrict_offchan = nla_get_u8(attr);
hdd_debug("%u", restrict_offchan);
if (restrict_offchan > 1) {
hdd_err("Invalid value %u", restrict_offchan);
return -EINVAL;
}
return wlan_hdd_handle_restrict_offchan_config(adapter,
restrict_offchan);
}
static int hdd_config_total_beacon_miss_count(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t first_miss_count;
uint8_t final_miss_count;
uint8_t total_miss_count;
QDF_STATUS status;
total_miss_count = nla_get_u8(attr);
ucfg_mlme_get_roam_bmiss_first_bcnt(hdd_ctx->psoc,
&first_miss_count);
if (total_miss_count <= first_miss_count) {
hdd_err("Total %u needs to exceed first %u",
total_miss_count, first_miss_count);
return -EINVAL;
}
final_miss_count = total_miss_count - first_miss_count;
hdd_debug("First count %u, final count %u",
first_miss_count, final_miss_count);
/*****
* TODO: research why is 0 being passed for vdev id???
*/
status = sme_set_roam_bmiss_final_bcnt(hdd_ctx->mac_handle,
0,
final_miss_count);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to set final count, status %u", status);
return qdf_status_to_os_return(status);
}
status = sme_set_bmiss_bcnt(adapter->vdev_id,
first_miss_count,
final_miss_count);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to set count, status %u", status);
return qdf_status_to_os_return(status);
}
static int hdd_config_latency_level(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint16_t latency_level;
QDF_STATUS status;
latency_level = nla_get_u16(attr);
switch (latency_level) {
case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL:
case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE:
case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
case QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
/* valid values */
break;
default:
hdd_err("Invalid value %u", latency_level);
return -EINVAL;
}
/* Map the latency value to the level which fw expected
* 0 - normal, 1 - moderate, 2 - low, 3 - ultralow
*/
latency_level = latency_level - 1;
status = sme_set_wlm_latency_level(hdd_ctx->mac_handle,
adapter->vdev_id,
latency_level);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("set latency level failed, %u", status);
return qdf_status_to_os_return(status);
}
static int hdd_config_disable_fils(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t disable_fils;
bool enabled;
QDF_STATUS status;
/* ignore unless in STA mode */
if (adapter->device_mode != QDF_STA_MODE)
return 0;
disable_fils = nla_get_u8(attr);
hdd_debug("%u", disable_fils);
enabled = !disable_fils;
status = ucfg_mlme_set_fils_enabled_info(hdd_ctx->psoc, enabled);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not set fils enabled info, %d", status);
status = ucfg_mlme_set_enable_bcast_probe_rsp(hdd_ctx->psoc, enabled);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not set enable bcast probe resp info, %d",
status);
status = wma_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE,
!disable_fils, VDEV_CMD);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("failed to set enable bcast probe resp, %d",
status);
return qdf_status_to_os_return(status);
}
static int hdd_config_rsn_ie(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
bool override_enabled;
uint8_t force_rsne_override;
/* ignore unless support explicitly enabled */
ucfg_mlme_get_force_rsne_override(hdd_ctx->psoc, &override_enabled);
if (!override_enabled)
return 0;
force_rsne_override = nla_get_u8(attr);
if (force_rsne_override > 1) {
hdd_err("Invalid value %d", force_rsne_override);
return -EINVAL;
}
hdd_ctx->force_rsne_override = force_rsne_override;
hdd_debug("force_rsne_override - %d", force_rsne_override);
return 0;
}
static int hdd_config_gtx(struct hdd_adapter *adapter,
const struct nlattr *attr)
{
uint8_t config_gtx;
int errno;
config_gtx = nla_get_u8(attr);
if (config_gtx > 1) {
hdd_err_rl("Invalid config_gtx value %d", config_gtx);
return -EINVAL;
}
errno = sme_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_GTX_ENABLE,
config_gtx, VDEV_CMD);
if (errno)
hdd_err("Failed to set GTX, %d", errno);
return errno;
}
/**
* typedef independent_setter_fn - independent attribute handler
* @adapter: The adapter being configured
* @attr: The nl80211 attribute being applied
*
* Defines the signature of functions in the independent attribute vtable
*
* Return: 0 if the attribute was handled successfully, otherwise an errno
*/
typedef int (*independent_setter_fn)(struct hdd_adapter *adapter,
const struct nlattr *attr);
/**
* struct independent_setters
* @id: vendor attribute which this entry handles
* @cb: callback function to invoke to process the attribute when present
*/
struct independent_setters {
uint32_t id;
independent_setter_fn cb;
};
/* vtable for independent setters */
static const struct independent_setters independent_setters[] = {
{QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
hdd_config_scan_default_ies},
{QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT,
hdd_config_fine_time_measurement},
{QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM,
hdd_config_modulated_dtim},
{QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL,
hdd_config_listen_interval},
{QCA_WLAN_VENDOR_ATTR_CONFIG_LRO,
hdd_config_lro},
{QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE,
hdd_config_scan_enable},
{QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER,
hdd_config_qpower},
{QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR,
hdd_config_stats_avg_factor},
{QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME,
hdd_config_guard_time},
{QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY,
hdd_config_non_agg_retry},
{QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY,
hdd_config_agg_retry},
{QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY,
hdd_config_mgmt_retry},
{QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY,
hdd_config_ctrl_retry},
{QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY,
hdd_config_propagation_delay},
{QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY,
hdd_config_propagation_abs_delay},
{QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT,
hdd_config_tx_fail_count},
{QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND,
hdd_config_channel_avoidance_ind},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA,
hdd_config_ant_div_ena},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF,
hdd_config_ant_div_snr_diff},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME,
hdd_config_ant_div_probe_dwell_time},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN,
hdd_config_ant_div_chain},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST,
hdd_config_ant_div_selftest},
{QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL,
hdd_config_ant_div_selftest_intvl},
{QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
hdd_config_ignore_assoc_disallowed},
{QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL,
hdd_config_restrict_offchannel},
{QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT,
hdd_config_total_beacon_miss_count},
{QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL,
hdd_config_latency_level},
{QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
hdd_config_disable_fils},
{QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE,
hdd_config_rsn_ie},
{QCA_WLAN_VENDOR_ATTR_CONFIG_GTX,
hdd_config_gtx},
};
/**
* hdd_set_independent_configuration() - Handle independent attributes
* @adapter: adapter upon which the vendor command was received
* @tb: parsed attribute array
*
* This is a table-driven function which dispatches independent
* attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION
* vendor command. An attribute is considered independent if it
* doesn't depend upon any other attributes
*
* Return: 0 if there were no issues, otherwise errno of the last issue
*/
static int hdd_set_independent_configuration(struct hdd_adapter *adapter,
struct nlattr **tb)
{
uint32_t i;
uint32_t id;
struct nlattr *attr;
independent_setter_fn cb;
int errno = 0;
int ret;
for (i = 0; i < QDF_ARRAY_SIZE(independent_setters); i++) {
id = independent_setters[i].id;
attr = tb[id];
if (!attr)
continue;
cb = independent_setters[i].cb;
ret = cb(adapter, attr);
if (ret)
errno = ret;
}
return errno;
}
/**
* typedef interdependent_setter_fn - interdependent attribute handler
* @adapter: The adapter being configured
* @tb: The parsed nl80211 attributes being applied
*
* Defines the signature of functions in the interdependent attribute vtable
*
* Return: 0 if attributes were handled successfully, otherwise an errno
*/
typedef int (*interdependent_setter_fn)(struct hdd_adapter *adapter,
struct nlattr **tb);
/* vtable for interdependent setters */
static const interdependent_setter_fn interdependent_setters[] = {
hdd_config_access_policy,
hdd_config_mpdu_aggregation,
hdd_config_ant_div_period,
hdd_config_ant_div_snr_weight,
wlan_hdd_cfg80211_wifi_set_reorder_timeout,
wlan_hdd_cfg80211_wifi_set_rx_blocksize,
};
/**
* hdd_set_interdependent_configuration() - Handle interdependent attributes
* @adapter: adapter upon which the vendor command was received
* @tb: parsed attribute array
*
* This is a table-driven function which handles interdependent
* attributes in a QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION
* vendor command. A set of attributes is considered interdependent if
* they depend upon each other. In the typical case if one of the
* attributes is present in the the attribute array, then all of the
* attributes must be present.
*
* Return: 0 if there were no issues, otherwise errno of the last issue
*/
static int hdd_set_interdependent_configuration(struct hdd_adapter *adapter,
struct nlattr **tb)
{
uint32_t i;
interdependent_setter_fn cb;
int errno = 0;
int ret;
for (i = 0; i < QDF_ARRAY_SIZE(interdependent_setters); i++) {
cb = interdependent_setters[i];
ret = cb(adapter, tb);
if (ret)
errno = ret;
}
return errno;
}
/**
* __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: Error code.
*/
static int
__wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1];
int errno;
int ret;
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data,
data_len, wlan_hdd_wifi_config_policy)) {
hdd_err("invalid attr");
return -EINVAL;
}
ret = hdd_set_independent_configuration(adapter, tb);
if (ret)
errno = ret;
ret = hdd_set_interdependent_configuration(adapter, tb);
if (ret)
errno = ret;
return errno;
}
/**
* wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX
*
* Return: Error code.
*/
static int
__wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX + 1];
int ret_val = 0;
uint8_t cfg_val = 0;
uint8_t set_val = 0;
struct sme_config_params *sme_config;
bool update_sme_cfg = false;
uint8_t tid = 0, ac;
uint16_t buff_size = 0;
mac_handle_t mac_handle;
QDF_STATUS status;
bool bval = false;
uint8_t value = 0;
uint8_t wmm_mode = 0;
uint32_t cmd_id;
hdd_enter_dev(dev);
sme_config = qdf_mem_malloc(sizeof(*sme_config));
if (!sme_config)
return -ENOMEM;
mac_handle = hdd_ctx->mac_handle;
sme_get_config_param(mac_handle, sme_config);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
ret_val = -EPERM;
goto send_err;
}
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
goto send_err;
if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) {
hdd_err("Driver Modules are closed, can not start logger");
ret_val = -EINVAL;
goto send_err;
}
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX,
data, data_len, wlan_hdd_wifi_test_config_policy)) {
hdd_err("invalid attr");
ret_val = -EINVAL;
goto send_err;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ]
);
hdd_debug("set addba accept req from peer value %d", cfg_val);
ret_val = sme_set_addba_accept(mac_handle, adapter->vdev_id,
cfg_val);
if (ret_val)
goto send_err;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS]);
hdd_debug("set HE MCS value 0x%0X", cfg_val);
ret_val = sme_update_he_mcs(mac_handle, adapter->vdev_id,
cfg_val);
if (ret_val)
goto send_err;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE]);
if (!cfg_val) {
sme_config->csr_config.WMMSupportMode =
hdd_to_csr_wmm_mode(HDD_WMM_USER_MODE_NO_QOS);
hdd_debug("wmm is disabled");
} else {
status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc,
&wmm_mode);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Get wmm_mode failed");
return QDF_STATUS_E_FAILURE;
}
sme_config->csr_config.WMMSupportMode =
hdd_to_csr_wmm_mode(wmm_mode);
hdd_debug("using wmm default value");
}
update_sme_cfg = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ]);
if (cfg_val) {
/*Auto BA mode*/
set_val = 0;
hdd_debug("BA operating mode is set to auto");
} else {
/*Manual BA mode*/
set_val = 1;
hdd_debug("BA operating mode is set to Manual");
}
ret_val = wma_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD);
if (ret_val) {
hdd_err("Set BA operating mode failed");
goto send_err;
}
if (!cfg_val) {
ret_val = wma_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_AMSDU_AGGREGATION_SIZE_OPTIMIZATION,
0, VDEV_CMD);
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION]
);
if (cfg_val > HE_FRAG_LEVEL1)
set_val = HE_FRAG_LEVEL1;
else
set_val = cfg_val;
hdd_debug("set HE fragmention to %d", set_val);
ret_val = sme_update_he_frag_supp(mac_handle,
adapter->vdev_id, set_val);
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE]);
sme_config->csr_config.wep_tkip_in_he = cfg_val;
hdd_debug("Set WEP/TKIP allow in HE %d", cfg_val);
update_sme_cfg = true;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]) {
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]) {
tid = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID]);
} else {
hdd_err("TID is not set for ADD/DEL BA cfg");
ret_val = -EINVAL;
goto send_err;
}
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION]);
if (cfg_val == QCA_WLAN_ADD_BA) {
if (tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE])
buff_size = nla_get_u16(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]);
ret_val = sme_send_addba_req(mac_handle,
adapter->vdev_id,
tid, buff_size);
} else if (cfg_val == QCA_WLAN_DELETE_BA) {
} else {
hdd_err("Invalid BA session cfg");
ret_val = -EINVAL;
goto send_err;
}
} else if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]) {
buff_size = nla_get_u16(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE]);
hdd_debug("set buff size to %d for all tids", buff_size);
ret_val = sme_set_ba_buff_size(mac_handle,
adapter->vdev_id, buff_size);
if (ret_val)
goto send_err;
if (buff_size > 64)
/* Configure ADDBA req buffer size to 256 */
set_val = 3;
else
/* Configure ADDBA req buffer size to 64 */
set_val = 2;
ret_val = wma_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_BA_MODE, set_val, VDEV_CMD);
if (ret_val)
hdd_err("Failed to set BA operating mode %d", set_val);
ret_val = wma_cli_set_command(adapter->vdev_id,
GEN_VDEV_PARAM_AMPDU,
buff_size, GEN_CMD);
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]) {
int he_mcs_val;
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]) {
ac = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC]);
} else {
hdd_err("AC is not set for NO ACK policy config");
ret_val = -EINVAL;
goto send_err;
}
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK]);
hdd_debug("Set NO_ACK to %d for ac %d", cfg_val, ac);
ret_val = sme_set_no_ack_policy(mac_handle,
adapter->vdev_id,
cfg_val, ac);
if (cfg_val) {
status = ucfg_mlme_get_vht_enable2x2(hdd_ctx->psoc,
&bval);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("unable to get vht_enable2x2");
if (bval)
/*2x2 MCS 5 value*/
he_mcs_val = 0x45;
else
/*1x1 MCS 5 value*/
he_mcs_val = 0x25;
if (hdd_set_11ax_rate(adapter, he_mcs_val, NULL))
hdd_err("HE MCS set failed, MCS val %0x",
he_mcs_val);
} else {
if (hdd_set_11ax_rate(adapter, 0xFF, NULL))
hdd_err("disable fixed rate failed");
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF]);
hdd_debug("Set HE LTF to %d", cfg_val);
ret_val = sme_set_auto_rate_he_ltf(mac_handle,
adapter->vdev_id,
cfg_val);
if (ret_val)
sme_err("Failed to set auto rate HE LTF");
ret_val = wma_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_HE_LTF,
cfg_val, VDEV_CMD);
if (ret_val)
goto send_err;
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE]);
hdd_debug("Set Tx beamformee to %d", cfg_val);
ret_val = sme_update_tx_bfee_supp(mac_handle,
adapter->vdev_id,
cfg_val);
if (ret_val)
sme_err("Failed to set Tx beamformee cap");
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS]);
status = ucfg_mlme_cfg_get_vht_tx_bfee_ant_supp(hdd_ctx->psoc,
&value);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("unable to get tx_bfee_ant_supp");
if (!cfg_in_range(CFG_VHT_BEAMFORMEE_ANT_SUPP, cfg_val)) {
hdd_err("NSTS %d not supported, supp_val %d", cfg_val,
value);
ret_val = -ENOTSUPP;
goto send_err;
}
hdd_debug("Set Tx beamformee NSTS to %d", cfg_val);
ret_val = sme_update_tx_bfee_nsts(hdd_ctx->mac_handle,
adapter->vdev_id,
cfg_val,
value);
if (ret_val)
sme_err("Failed to set Tx beamformee cap");
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR]);
if (cfg_val) {
hdd_debug("Set HE mac padding dur to %d", cfg_val);
ret_val = sme_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN,
0, VDEV_CMD);
if (ret_val)
hdd_err("MU_EDCA update disable failed");
sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true);
sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle);
if (sme_update_mu_edca_params(hdd_ctx->mac_handle,
adapter->vdev_id))
hdd_err("Failed to send mu edca params");
} else {
ret_val = sme_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN,
1, VDEV_CMD);
sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false);
}
ret_val = sme_update_he_trigger_frm_mac_pad(hdd_ctx->mac_handle,
adapter->vdev_id,
cfg_val);
if (ret_val)
hdd_err("Failed to set Trig frame mac padding cap");
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA]);
if (cfg_val) {
ret_val = sme_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN,
0, VDEV_CMD);
if (ret_val)
hdd_err("MU_EDCA update disable failed");
sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, true);
sme_set_he_mu_edca_def_cfg(hdd_ctx->mac_handle);
if (sme_update_mu_edca_params(hdd_ctx->mac_handle,
adapter->vdev_id))
hdd_err("Failed to send mu edca params");
} else {
ret_val = sme_cli_set_command(adapter->vdev_id,
WMI_VDEV_PARAM_MU_EDCA_FW_UPDATE_EN,
1, VDEV_CMD);
sme_set_usr_cfg_mu_edca(hdd_ctx->mac_handle, false);
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]) {
cfg_val = nla_get_u8(tb[
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP]);
ret_val = sme_update_he_om_ctrl_supp(hdd_ctx->mac_handle,
adapter->vdev_id,
cfg_val);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX;
if (tb[cmd_id]) {
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX + 1];
struct nlattr *curr_attr;
int tmp, rc;
nla_for_each_nested(curr_attr, tb[cmd_id], tmp) {
rc = wlan_cfg80211_nla_parse(
tb2, QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX,
nla_data(curr_attr),
nla_len(curr_attr),
qca_wlan_vendor_attr_he_omi_tx_policy);
if (rc) {
hdd_err("Invalid ATTR");
goto send_err;
}
cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW;
if (tb2[cmd_id]) {
cfg_val = nla_get_u8(tb2[cmd_id]);
ret_val = sme_set_he_om_ctrl_param(
hdd_ctx->mac_handle,
adapter->vdev_id,
cmd_id, cfg_val);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS;
if (tb2[cmd_id]) {
cfg_val = nla_get_u8(tb2[cmd_id]);
ret_val = sme_set_he_om_ctrl_param(
hdd_ctx->mac_handle,
adapter->vdev_id,
cmd_id, cfg_val);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE;
if (tb2[cmd_id]) {
cfg_val = nla_get_u8(tb2[cmd_id]);
ret_val = sme_set_he_om_ctrl_param(
hdd_ctx->mac_handle,
adapter->vdev_id,
cmd_id, cfg_val);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS;
if (tb2[cmd_id]) {
cfg_val = nla_get_u8(tb2[cmd_id]);
ret_val = sme_set_he_om_ctrl_param(
hdd_ctx->mac_handle,
adapter->vdev_id,
cmd_id, cfg_val);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE;
if (tb2[cmd_id]) {
cfg_val = nla_get_u8(tb2[cmd_id]);
ret_val = sme_set_he_om_ctrl_param(
hdd_ctx->mac_handle,
adapter->vdev_id,
cmd_id, cfg_val);
}
}
if (ret_val) {
sme_reset_he_om_ctrl(hdd_ctx->mac_handle);
goto send_err;
}
ret_val = sme_send_he_om_ctrl_update(hdd_ctx->mac_handle,
adapter->vdev_id);
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG])
sme_reset_he_om_ctrl(hdd_ctx->mac_handle);
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("Configure Tx SU PPDU enable %d", cfg_val);
if (cfg_val)
sme_config_su_ppdu_queue(adapter->vdev_id, true);
else
sme_config_su_ppdu_queue(adapter->vdev_id, false);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("Configure 2G VHT support %d", cfg_val);
ucfg_mlme_set_vht_for_24ghz(hdd_ctx->psoc,
(cfg_val ? true : false));
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("Configure +HTC_HE support %d", cfg_val);
sme_update_he_htc_he_supp(hdd_ctx->mac_handle,
adapter->vdev_id,
(cfg_val ? true : false));
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("Configure HE testbed defaults %d", cfg_val);
if (!cfg_val)
sme_reset_he_caps(hdd_ctx->mac_handle,
adapter->vdev_id);
else
sme_set_he_testbed_def(hdd_ctx->mac_handle,
adapter->vdev_id);
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("Configure Action frame Tx in TB PPDU %d", cfg_val);
sme_config_action_tx_in_tb_ppdu(hdd_ctx->mac_handle,
adapter->vdev_id, cfg_val);
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP]) {
struct wmi_twt_add_dialog_param params = {0};
struct hdd_station_ctx *hdd_sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
uint32_t wake_intvl_exp = 0, result = 0;
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX + 1];
struct nlattr *twt_session;
int tmp, rc;
if ((adapter->device_mode != QDF_STA_MODE &&
adapter->device_mode != QDF_P2P_CLIENT_MODE) ||
hdd_sta_ctx->conn_info.conn_state !=
eConnectionState_Associated) {
hdd_err_rl("Invalid state, vdev %d mode %d state %d",
adapter->vdev_id, adapter->device_mode,
hdd_sta_ctx->conn_info.conn_state);
goto send_err;
}
qdf_mem_copy(params.peer_macaddr,
hdd_sta_ctx->conn_info.bssid.bytes,
QDF_MAC_ADDR_SIZE);
params.vdev_id = adapter->vdev_id;
params.dialog_id = 0;
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP;
nla_for_each_nested(twt_session, tb[cmd_id], tmp) {
rc = wlan_cfg80211_nla_parse(
tb2, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX,
nla_data(twt_session),
nla_len(twt_session),
qca_wlan_vendor_twt_add_dialog_policy);
if (rc) {
hdd_err_rl("Invalid twt ATTR");
goto send_err;
}
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP;
if (!tb2[cmd_id]) {
hdd_err_rl("TWT_SETUP_WAKE_INTVL_EXP is must");
goto send_err;
}
wake_intvl_exp = nla_get_u8(tb2[cmd_id]);
if (wake_intvl_exp > TWT_SETUP_WAKE_INTVL_EXP_MAX) {
hdd_err_rl("Invalid wake_intvl_exp %u > %u",
wake_intvl_exp,
TWT_SETUP_WAKE_INTVL_EXP_MAX);
goto send_err;
}
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST;
if (tb2[cmd_id])
params.flag_bcast = nla_get_flag(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE;
if (!tb2[cmd_id]) {
hdd_err_rl("TWT_SETUP_REQ_TYPE is must");
goto send_err;
}
params.twt_cmd = nla_get_u8(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER;
if (tb2[cmd_id])
params.flag_trigger = nla_get_flag(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE;
if (!tb2[cmd_id]) {
hdd_err_rl("TWT_SETUP_FLOW_TYPE is must");
goto send_err;
}
params.flag_flow_type = nla_get_u8(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION;
if (tb2[cmd_id])
params.flag_protection =
nla_get_flag(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME;
if (tb2[cmd_id])
params.sp_offset_us = nla_get_u32(tb2[cmd_id]);
cmd_id = QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION;
if (!tb2[cmd_id]) {
hdd_err_rl("TWT_SETUP_WAKE_DURATION is must");
goto send_err;
}
params.wake_dura_us = 256 * nla_get_u32(tb2[cmd_id]);
if (params.wake_dura_us > TWT_SETUP_WAKE_DURATION_MAX) {
hdd_err_rl("Invalid wake_dura_us %u",
params.wake_dura_us);
goto send_err;
}
cmd_id =
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA;
if (!tb2[cmd_id]) {
hdd_err_rl("SETUP_WAKE_INTVL_MANTISSA is must");
goto send_err;
}
params.wake_intvl_mantis = nla_get_u32(tb2[cmd_id]);
if (params.wake_intvl_mantis >
TWT_SETUP_WAKE_INTVL_MANTISSA_MAX) {
hdd_err_rl("Invalid wake_intvl_mantis %u",
params.wake_dura_us);
goto send_err;
}
if (wake_intvl_exp && params.wake_intvl_mantis) {
result = 2 << (wake_intvl_exp - 1);
if (result >
(UINT_MAX / params.wake_intvl_mantis)) {
hdd_err_rl("Invalid exp %d mantissa %d",
wake_intvl_exp,
params.wake_intvl_mantis);
goto send_err;
}
params.wake_intvl_us =
params.wake_intvl_mantis * result;
} else {
params.wake_intvl_us = params.wake_intvl_mantis;
}
hdd_debug("twt: vdev %d, intvl_us %d, mantis %d",
params.vdev_id, params.wake_intvl_us,
params.wake_intvl_mantis);
hdd_debug("twt: dura %d, offset %d, cmd %d",
params.wake_dura_us, params.sp_offset_us,
params.twt_cmd);
hdd_debug("twt: bcast %d, trigger %d, type %d, prot %d",
params.flag_bcast, params.flag_trigger,
params.flag_flow_type,
params.flag_protection);
ret_val = qdf_status_to_os_return(
wma_twt_process_add_dialog(&params));
if (ret_val)
goto send_err;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE]) {
struct wmi_twt_del_dialog_param params = {0};
struct hdd_station_ctx *hdd_sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if ((adapter->device_mode != QDF_STA_MODE &&
adapter->device_mode != QDF_P2P_CLIENT_MODE) ||
hdd_sta_ctx->conn_info.conn_state !=
eConnectionState_Associated) {
hdd_err("Invalid state, vdev %d mode %d state %d",
adapter->vdev_id, adapter->device_mode,
hdd_sta_ctx->conn_info.conn_state);
goto send_err;
}
qdf_mem_copy(params.peer_macaddr,
hdd_sta_ctx->conn_info.bssid.bytes,
QDF_MAC_ADDR_SIZE);
params.vdev_id = adapter->vdev_id;
params.dialog_id = 0;
hdd_debug("twt_terminate: vdev_id %d", params.vdev_id);
ret_val = qdf_status_to_os_return(
wma_twt_process_del_dialog(&params));
if (ret_val)
goto send_err;
}
cmd_id = QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT;
if (tb[cmd_id]) {
cfg_val = nla_get_u8(tb[cmd_id]);
hdd_debug("twt_request: val %d", cfg_val);
ret_val = sme_update_he_twt_req_support(hdd_ctx->mac_handle,
adapter->vdev_id,
cfg_val);
}
if (update_sme_cfg)
sme_update_config(mac_handle, sme_config);
send_err:
qdf_mem_free(sme_config);
return ret_val;
}
/**
* wlan_hdd_cfg80211_set_wifi_test_config() - Wifi test configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_set_wifi_test_config(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_wifi_test_config(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
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 enables or disables the collection of packet statistics from
* the firmware
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
QDF_STATUS status;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1];
struct sir_wifi_start_log start_log = { 0 };
mac_handle_t mac_handle;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) {
hdd_err("Driver Modules are closed, can not start logger");
return -EINVAL;
}
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_start_policy)) {
hdd_err("Invalid attribute");
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) {
hdd_err("attr ATTR failed");
return -EINVAL;
}
start_log.ring_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]);
hdd_debug("Ring ID=%d", start_log.ring_id);
/* Parse and fetch verbose level */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) {
hdd_err("attr verbose_level failed");
return -EINVAL;
}
start_log.verbose_level = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]);
hdd_debug("verbose_level=%d", start_log.verbose_level);
/* Parse and fetch flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) {
hdd_err("attr flag failed");
return -EINVAL;
}
start_log.is_iwpriv_command = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]);
start_log.user_triggered = 1;
/* size is buff size which can be set using iwpriv command*/
start_log.size = 0;
start_log.is_pktlog_buff_clear = false;
cds_set_ring_log_level(start_log.ring_id, start_log.verbose_level);
if (start_log.ring_id == RING_ID_WAKELOCK) {
/* Start/stop wakelock events */
if (start_log.verbose_level > WLAN_LOG_LEVEL_OFF)
cds_set_wakelock_logging(true);
else
cds_set_wakelock_logging(false);
return 0;
}
mac_handle = hdd_ctx->mac_handle;
status = sme_wifi_start_logger(mac_handle, start_log);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_wifi_start_logger failed(err=%d)",
status);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_wifi_logger_start() - Wrapper function used to enable
* or disable the collection of packet statistics from the firmware
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to enable or disable the collection of packet
* statistics from the firmware
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_wifi_logger_start(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
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)
{
QDF_STATUS status;
uint32_t ring_id;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1];
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_get_ring_data_policy)) {
hdd_err("Invalid attribute");
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) {
hdd_err("attr ATTR failed");
return -EINVAL;
}
ring_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]);
if (ring_id == RING_ID_PER_PACKET_STATS) {
wlan_logging_set_per_pkt_stats();
hdd_debug("Flushing/Retrieving packet stats");
} else if (ring_id == RING_ID_DRIVER_DEBUG) {
/*
* As part of DRIVER ring ID, flush both driver and fw logs.
* For other Ring ID's driver doesn't have any rings to flush
*/
hdd_info("Bug report triggered by framework");
status = cds_flush_logs(WLAN_LOG_TYPE_NON_FATAL,
WLAN_LOG_INDICATOR_FRAMEWORK,
WLAN_LOG_REASON_CODE_UNUSED,
true, false);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Failed to trigger bug report");
return -EINVAL;
}
} else {
wlan_report_log_completion(WLAN_LOG_TYPE_NON_FATAL,
WLAN_LOG_INDICATOR_FRAMEWORK,
WLAN_LOG_REASON_CODE_UNUSED,
ring_id);
}
return 0;
}
/**
* wlan_hdd_cfg80211_wifi_logger_get_ring_data() - Wrapper to flush packet stats
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to flush or retrieve the per packet statistics from
* the driver
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
/**
* hdd_map_req_id_to_pattern_id() - map request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* if the slot is available, store the request id and return pattern id
* if entry exists, return the pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_map_req_id_to_pattern_id(struct hdd_context *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) {
if (hdd_ctx->op_ctx.op_table[i].request_id == MAX_REQUEST_ID) {
hdd_ctx->op_ctx.op_table[i].request_id = request_id;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
} else if (hdd_ctx->op_ctx.op_table[i].request_id ==
request_id) {
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -ENOBUFS;
}
/**
* hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* reset request id to 0 (slot available again) and
* return pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_unmap_req_id_to_pattern_id(struct hdd_context *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++) {
if (hdd_ctx->op_ctx.op_table[i].request_id == request_id) {
hdd_ctx->op_ctx.op_table[i].request_id = MAX_REQUEST_ID;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -EINVAL;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID
#define PARAM_CONTROL \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL
#define PARAM_IP_PACKET \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA
#define PARAM_SRC_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR
#define PARAM_DST_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR
#define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD
#define PARAM_PROTO_TYPE \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE
/**
* wlan_hdd_add_tx_ptrn() - add tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a AddTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_add_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx,
struct nlattr **tb)
{
struct sSirAddPeriodicTxPtrn *add_req;
QDF_STATUS status;
uint32_t request_id, len;
int32_t ret;
uint8_t pattern_id = 0;
struct qdf_mac_addr dst_addr;
uint16_t eth_type = htons(ETH_P_IP);
mac_handle_t mac_handle;
if (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
hdd_err("Not in Connected state!");
return -ENOTSUPP;
}
add_req = qdf_mem_malloc(sizeof(*add_req));
if (!add_req)
return -ENOMEM;
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hdd_err("attr request id failed");
ret = -EINVAL;
goto fail;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
if (request_id == MAX_REQUEST_ID) {
hdd_err("request_id cannot be MAX");
ret = -EINVAL;
goto fail;
}
hdd_debug("Request Id: %u", request_id);
if (!tb[PARAM_PERIOD]) {
hdd_err("attr period failed");
ret = -EINVAL;
goto fail;
}
add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]);
hdd_debug("Period: %u ms", add_req->usPtrnIntervalMs);
if (add_req->usPtrnIntervalMs == 0) {
hdd_err("Invalid interval zero, return failure");
ret = -EINVAL;
goto fail;
}
if (!tb[PARAM_SRC_MAC_ADDR]) {
hdd_err("attr source mac address failed");
ret = -EINVAL;
goto fail;
}
nla_memcpy(add_req->mac_address.bytes, tb[PARAM_SRC_MAC_ADDR],
QDF_MAC_ADDR_SIZE);
hdd_debug("input src mac address: "QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(add_req->mac_address.bytes));
if (!qdf_is_macaddr_equal(&add_req->mac_address,
&adapter->mac_addr)) {
hdd_err("input src mac address and connected ap bssid are different");
ret = -EINVAL;
goto fail;
}
if (!tb[PARAM_DST_MAC_ADDR]) {
hdd_err("attr dst mac address failed");
ret = -EINVAL;
goto fail;
}
nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], QDF_MAC_ADDR_SIZE);
hdd_debug("input dst mac address: "QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(dst_addr.bytes));
if (!tb[PARAM_IP_PACKET]) {
hdd_err("attr ip packet failed");
ret = -EINVAL;
goto fail;
}
add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]);
hdd_debug("IP packet len: %u", add_req->ucPtrnSize);
if (add_req->ucPtrnSize < 0 ||
add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE -
ETH_HLEN)) {
hdd_err("Invalid IP packet len: %d",
add_req->ucPtrnSize);
ret = -EINVAL;
goto fail;
}
if (!tb[PARAM_PROTO_TYPE])
eth_type = htons(ETH_P_IP);
else
eth_type = htons(nla_get_u16(tb[PARAM_PROTO_TYPE]));
hdd_debug("packet proto type: %u", eth_type);
len = 0;
qdf_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, QDF_MAC_ADDR_SIZE);
len += QDF_MAC_ADDR_SIZE;
qdf_mem_copy(&add_req->ucPattern[len], add_req->mac_address.bytes,
QDF_MAC_ADDR_SIZE);
len += QDF_MAC_ADDR_SIZE;
qdf_mem_copy(&add_req->ucPattern[len], &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 |
* ------------------------------------------------------------
*/
qdf_mem_copy(&add_req->ucPattern[len],
nla_data(tb[PARAM_IP_PACKET]),
add_req->ucPtrnSize);
add_req->ucPtrnSize += len;
ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret) {
hdd_err("req id to pattern id failed (ret=%d)", ret);
goto fail;
}
add_req->ucPtrnId = pattern_id;
hdd_debug("pattern id: %d", add_req->ucPtrnId);
mac_handle = hdd_ctx->mac_handle;
status = sme_add_periodic_tx_ptrn(mac_handle, add_req);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_add_periodic_tx_ptrn failed (err=%d)", status);
ret = qdf_status_to_os_return(status);
goto fail;
}
hdd_exit();
fail:
qdf_mem_free(add_req);
return ret;
}
/**
* wlan_hdd_del_tx_ptrn() - delete tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a DelTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_del_tx_ptrn(struct hdd_adapter *adapter, struct hdd_context *hdd_ctx,
struct nlattr **tb)
{
struct sSirDelPeriodicTxPtrn *del_req;
QDF_STATUS status;
uint32_t request_id, ret;
uint8_t pattern_id = 0;
mac_handle_t mac_handle;
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hdd_err("attr request id failed");
return -EINVAL;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
if (request_id == MAX_REQUEST_ID) {
hdd_err("request_id cannot be MAX");
return -EINVAL;
}
ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret) {
hdd_err("req id to pattern id failed (ret=%d)", ret);
return -EINVAL;
}
del_req = qdf_mem_malloc(sizeof(*del_req));
if (!del_req)
return -ENOMEM;
qdf_copy_macaddr(&del_req->mac_address, &adapter->mac_addr);
hdd_debug(QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(del_req->mac_address.bytes));
del_req->ucPtrnId = pattern_id;
hdd_debug("Request Id: %u Pattern id: %d",
request_id, del_req->ucPtrnId);
mac_handle = hdd_ctx->mac_handle;
status = sme_del_periodic_tx_ptrn(mac_handle, del_req);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_del_periodic_tx_ptrn failed (err=%d)", status);
goto fail;
}
hdd_exit();
qdf_mem_free(del_req);
return 0;
fail:
qdf_mem_free(del_req);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
uint8_t control;
int ret;
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 = QDF_MAC_ADDR_SIZE },
[PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY,
.len = QDF_MAC_ADDR_SIZE },
[PARAM_PERIOD] = { .type = NLA_U32 },
[PARAM_PROTO_TYPE] = {.type = NLA_U16},
};
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) {
hdd_err("Periodic Tx Pattern Offload feature is not supported in FW!");
return -ENOTSUPP;
}
if (wlan_cfg80211_nla_parse(tb, PARAM_MAX, data, data_len, policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[PARAM_CONTROL]) {
hdd_err("attr control failed");
return -EINVAL;
}
control = nla_get_u32(tb[PARAM_CONTROL]);
hdd_debug("Control: %d", control);
if (control == WLAN_START_OFFLOADED_PACKETS)
return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb);
if (control == WLAN_STOP_OFFLOADED_PACKETS)
return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb);
hdd_err("Invalid control: %d", control);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_CONTROL
#undef PARAM_IP_PACKET
#undef PARAM_SRC_MAC_ADDR
#undef PARAM_DST_MAC_ADDR
#undef PARAM_PERIOD
#undef PARAM_PROTO_TYPE
/**
* wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_offloaded_packets(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
#ifdef WLAN_NS_OFFLOAD
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];
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_enter_dev(wdev->netdev);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
if (!ucfg_pmo_is_ns_offloaded(hdd_ctx->psoc)) {
hdd_err("ND Offload not supported");
return -EINVAL;
}
if (!ucfg_pmo_is_active_mode_offloaded(hdd_ctx->psoc)) {
hdd_warn("Active mode offload is disabled");
return -EINVAL;
}
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX,
(struct nlattr *)data, data_len,
ns_offload_set_policy)) {
hdd_err("nla_parse failed");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]) {
hdd_err("ND Offload flag attribute not present");
return -EINVAL;
}
hdd_ctx->ns_offload_enable =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG]);
if (QDF_IBSS_MODE == adapter->device_mode) {
hdd_debug("NS Offload is not supported in IBSS mode");
return -EINVAL;
}
/* update ns offload in case it is already enabled/disabled */
if (hdd_ctx->ns_offload_enable)
hdd_enable_ns_offload(adapter, pmo_ns_offload_dynamic_update);
else
hdd_disable_ns_offload(adapter, pmo_ns_offload_dynamic_update);
return 0;
}
/**
* wlan_hdd_cfg80211_set_ns_offload() - enable/disable NS offload
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* Return: Return the Success or Failure code.
*/
static int wlan_hdd_cfg80211_set_ns_offload(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_ns_offload(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif /* WLAN_NS_OFFLOAD */
/**
* struct weighed_pcl: Preferred channel info
* @freq: Channel frequency
* @weight: Weightage of the channel
* @flag: Validity of the channel in p2p negotiation
*/
struct weighed_pcl {
u32 freq;
u32 weight;
u32 flag;
};
static const struct nla_policy get_preferred_freq_list_policy
[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE] = {
.type = NLA_U32},
};
static uint32_t wlan_hdd_populate_weigh_pcl(
struct policy_mgr_pcl_chan_weights *
chan_weights,
struct weighed_pcl *w_pcl,
enum policy_mgr_con_mode intf_mode)
{
int i, j;
uint32_t chan_idx = 0;
uint32_t set = 0;
uint32_t pcl_len = chan_weights->pcl_len;
uint32_t valid_weight;
/* convert channel number to frequency */
for (i = 0; i < chan_weights->pcl_len; i++) {
if (chan_weights->pcl_list[i] <=
ARRAY_SIZE(hdd_channels_2_4_ghz))
w_pcl[i].freq = ieee80211_channel_to_frequency(
chan_weights->pcl_list[i],
HDD_NL80211_BAND_2GHZ);
else
w_pcl[i].freq = ieee80211_channel_to_frequency(
chan_weights->pcl_list[i],
HDD_NL80211_BAND_5GHZ);
w_pcl[i].weight = chan_weights->weight_list[i];
if (intf_mode == PM_SAP_MODE || intf_mode == PM_P2P_GO_MODE)
w_pcl[i].flag = set | PCL_CHANNEL_SUPPORT_GO;
else
w_pcl[i].flag = set | PCL_CHANNEL_SUPPORT_CLI;
}
chan_idx = pcl_len;
if (chan_weights->weight_list[pcl_len - 1] >
PCL_GROUPS_WEIGHT_DIFFERENCE)
/* Set non-pcl channels weight 20 point less than the last PCL entry */
valid_weight = chan_weights->weight_list[pcl_len - 1] -
PCL_GROUPS_WEIGHT_DIFFERENCE;
else
valid_weight = 1;
/* Include rest of the valid channels */
for (i = 0; i < chan_weights->saved_num_chan; i++) {
for (j = 0; j < chan_weights->pcl_len; j++) {
if (chan_weights->saved_chan_list[i] ==
chan_weights->pcl_list[j])
break;
}
if (j == chan_weights->pcl_len) {
if (chan_weights->saved_chan_list[i] <=
ARRAY_SIZE(hdd_channels_2_4_ghz))
w_pcl[chan_idx].freq =
ieee80211_channel_to_frequency(
chan_weights->saved_chan_list[i],
HDD_NL80211_BAND_2GHZ);
else
w_pcl[chan_idx].freq =
ieee80211_channel_to_frequency(
chan_weights->saved_chan_list[i],
HDD_NL80211_BAND_5GHZ);
if (!chan_weights->weighed_valid_list[i]) {
w_pcl[chan_idx].flag =
set | PCL_CHANNEL_EXCLUDE_IN_GO_NEG;
w_pcl[chan_idx].weight = 0;
} else {
if (intf_mode == PM_SAP_MODE ||
intf_mode == PM_P2P_GO_MODE)
w_pcl[chan_idx].flag =
set | PCL_CHANNEL_SUPPORT_GO;
else
w_pcl[chan_idx].flag =
set | PCL_CHANNEL_SUPPORT_CLI;
w_pcl[chan_idx].weight = valid_weight;
}
chan_idx++;
}
}
return chan_idx;
}
/** __wlan_hdd_cfg80211_get_preferred_freq_list() - get preferred frequency list
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* This function return the preferred frequency list generated by the policy
* manager.
*
* Return: success or failure code
*/
static int __wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy,
struct wireless_dev
*wdev, const void *data,
int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int i, ret = 0;
QDF_STATUS status;
uint32_t pcl_len = 0;
uint32_t freq_list[QDF_MAX_NUM_CHAN];
enum policy_mgr_con_mode intf_mode;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX + 1];
struct sk_buff *reply_skb;
struct weighed_pcl *w_pcl;
struct nlattr *nla_attr, *channel;
struct policy_mgr_pcl_chan_weights *chan_weights;
hdd_enter_dev(wdev->netdev);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX,
data, data_len,
get_preferred_freq_list_policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]) {
hdd_err("attr interface type failed");
return -EINVAL;
}
intf_mode = nla_get_u32(tb
[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE]);
if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) {
hdd_err("Invalid interface type");
return -EINVAL;
}
hdd_debug("Userspace requested pref freq list");
chan_weights =
qdf_mem_malloc(sizeof(struct policy_mgr_pcl_chan_weights));
if (!chan_weights)
return -ENOMEM;
status = policy_mgr_get_pcl(hdd_ctx->psoc,
intf_mode, chan_weights->pcl_list,
&chan_weights->pcl_len,
chan_weights->weight_list,
QDF_ARRAY_SIZE(chan_weights->weight_list));
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Get pcl failed");
qdf_mem_free(chan_weights);
return -EINVAL;
}
sme_get_valid_channels(chan_weights->saved_chan_list,
&chan_weights->saved_num_chan);
policy_mgr_get_valid_chan_weights(hdd_ctx->psoc, chan_weights);
w_pcl = qdf_mem_malloc(sizeof(struct weighed_pcl) * QDF_MAX_NUM_CHAN);
if (!w_pcl) {
qdf_mem_free(chan_weights);
return -ENOMEM;
}
pcl_len = wlan_hdd_populate_weigh_pcl(chan_weights, w_pcl, intf_mode);
qdf_mem_free(chan_weights);
for (i = 0; i < pcl_len; i++)
freq_list[i] = w_pcl[i].freq;
/* send the freq_list back to supplicant */
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
sizeof(u32) * pcl_len +
sizeof(struct weighed_pcl) * pcl_len +
NLMSG_HDRLEN);
if (!reply_skb) {
hdd_err("Allocate reply_skb failed");
qdf_mem_free(w_pcl);
return -EINVAL;
}
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
intf_mode) ||
nla_put(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
sizeof(uint32_t) * pcl_len,
freq_list)) {
hdd_err("nla put fail");
kfree_skb(reply_skb);
qdf_mem_free(w_pcl);
return -EINVAL;
}
i = QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL;
nla_attr = nla_nest_start(reply_skb, i);
if (!nla_attr) {
hdd_err("nla nest start fail");
kfree_skb(reply_skb);
qdf_mem_free(w_pcl);
return -EINVAL;
}
for (i = 0; i < pcl_len; i++) {
channel = nla_nest_start(reply_skb, i);
if (!channel) {
hdd_err("updating pcl list failed");
kfree_skb(reply_skb);
qdf_mem_free(w_pcl);
return -EINVAL;
}
if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FREQ,
w_pcl[i].freq) ||
nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT,
w_pcl[i].weight) ||
nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_PCL_FLAG,
w_pcl[i].flag)) {
hdd_err("nla put fail");
kfree_skb(reply_skb);
qdf_mem_free(w_pcl);
return -EINVAL;
}
nla_nest_end(reply_skb, channel);
}
nla_nest_end(reply_skb, nla_attr);
qdf_mem_free(w_pcl);
return cfg80211_vendor_cmd_reply(reply_skb);
}
/** wlan_hdd_cfg80211_get_preferred_freq_list () - get preferred frequency list
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* This function return the preferred frequency list generated by the policy
* manager.
*
* Return: success or failure code
*/
static int wlan_hdd_cfg80211_get_preferred_freq_list(struct wiphy *wiphy,
struct wireless_dev
*wdev, const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_preferred_freq_list(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
static const struct nla_policy set_probable_oper_channel_policy
[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE] = {
.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ] = {
.type = NLA_U32},
};
/**
* __wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *ndev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int ret = 0;
enum policy_mgr_con_mode intf_mode;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX + 1];
uint32_t channel_hint;
hdd_enter_dev(ndev);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX,
data, data_len,
set_probable_oper_channel_policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]) {
hdd_err("attr interface type failed");
return -EINVAL;
}
intf_mode = nla_get_u32(tb
[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE]);
if (intf_mode < PM_STA_MODE || intf_mode >= PM_MAX_NUM_OF_MODE) {
hdd_err("Invalid interface type");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]) {
hdd_err("attr probable freq failed");
return -EINVAL;
}
channel_hint = cds_freq_to_chan(nla_get_u32(tb
[QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ]));
/* check pcl table */
if (!policy_mgr_allow_concurrency(hdd_ctx->psoc, intf_mode,
channel_hint, HW_MODE_20_MHZ)) {
hdd_err("Set channel hint failed due to concurrency check");
return -EINVAL;
}
if (0 != wlan_hdd_check_remain_on_channel(adapter))
hdd_warn("Remain On Channel Pending");
if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, channel_hint,
POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) {
hdd_err("Failed to change hw mode");
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_set_probable_oper_channel () - set probable channel
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_set_probable_oper_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_probable_oper_channel(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
static const struct
nla_policy
qca_wlan_vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {
.type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE },
};
/**
* __wlan_hdd_cfg80211_get_link_properties() - Get link properties
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to get link properties like nss, rate flags and
* operating frequency for the active connection with the given peer.
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_station_ctx *hdd_sta_ctx;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1];
uint8_t peer_mac[QDF_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;
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (0 != wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data,
data_len, qca_wlan_vendor_attr_policy)) {
hdd_err("Invalid attribute");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hdd_err("Attribute peerMac not provided for mode=%d",
adapter->device_mode);
return -EINVAL;
}
if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < QDF_MAC_ADDR_SIZE) {
hdd_err("Attribute peerMac is invalid for mode=%d",
adapter->device_mode);
return -EINVAL;
}
qdf_mem_copy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
QDF_MAC_ADDR_SIZE);
hdd_debug("peerMac="QDF_MAC_ADDR_STR" for device_mode:%d",
QDF_MAC_ADDR_ARRAY(peer_mac), adapter->device_mode);
if (adapter->device_mode == QDF_STA_MODE ||
adapter->device_mode == QDF_P2P_CLIENT_MODE) {
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if ((hdd_sta_ctx->conn_info.conn_state !=
eConnectionState_Associated) ||
qdf_mem_cmp(hdd_sta_ctx->conn_info.bssid.bytes,
peer_mac, QDF_MAC_ADDR_SIZE)) {
hdd_err("Not Associated to mac "QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = hdd_sta_ctx->conn_info.nss;
freq = cds_chan_to_freq(
hdd_sta_ctx->conn_info.channel);
rate_flags = hdd_sta_ctx->conn_info.rate_flags;
} else if (adapter->device_mode == QDF_P2P_GO_MODE ||
adapter->device_mode == QDF_SAP_MODE) {
for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) {
if (adapter->sta_info[sta_id].in_use &&
!qdf_is_macaddr_broadcast(
&adapter->sta_info[sta_id].sta_mac) &&
!qdf_mem_cmp(
&adapter->sta_info[sta_id].sta_mac.bytes,
peer_mac, QDF_MAC_ADDR_SIZE))
break;
}
if (WLAN_MAX_STA_COUNT == sta_id) {
hdd_err("No active peer with mac="QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = adapter->sta_info[sta_id].nss;
freq = cds_chan_to_freq(
(WLAN_HDD_GET_AP_CTX_PTR(adapter))->operating_channel);
rate_flags = adapter->sta_info[sta_id].rate_flags;
} else {
hdd_err("Not Associated! with mac "QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
if (!(rate_flags & TX_RATE_LEGACY)) {
if (rate_flags & TX_RATE_VHT80) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
} else if (rate_flags & TX_RATE_VHT40) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
} else if (rate_flags & TX_RATE_VHT20) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
} else if (rate_flags &
(TX_RATE_HT20 | TX_RATE_HT40)) {
final_rate_flags |= RATE_INFO_FLAGS_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
if (rate_flags & TX_RATE_HT40)
final_rate_flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
if (rate_flags & TX_RATE_SGI) {
if (!(final_rate_flags & RATE_INFO_FLAGS_VHT_MCS))
final_rate_flags |= RATE_INFO_FLAGS_MCS;
final_rate_flags |= RATE_INFO_FLAGS_SHORT_GI;
}
}
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(u8) + sizeof(u8) + sizeof(u32) + NLMSG_HDRLEN);
if (!reply_skb) {
hdd_err("getLinkProperties: skb alloc failed");
return -EINVAL;
}
if (nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_NSS,
nss) ||
nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_RATE_FLAGS,
final_rate_flags) ||
nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_FREQ,
freq)) {
hdd_err("nla_put failed");
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* wlan_hdd_cfg80211_get_link_properties() - Wrapper function to get link
* properties.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to get link properties like nss, rate flags and
* operating frequency for the active connection with the given peer.
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_link_properties(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
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 },
[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST] = {
.type = NLA_NESTED },
};
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)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX + 1];
int ret;
struct acs_dfs_policy *acs_policy;
int mode = DFS_MODE_NONE;
int channel_hint = 0;
hdd_enter_dev(wdev->netdev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX,
data, data_len,
wlan_hdd_set_acs_dfs_config_policy)) {
hdd_err("invalid attr");
return -EINVAL;
}
acs_policy = &hdd_ctx->acs_policy;
/*
* SCM sends this attribute to restrict SAP from choosing
* DFS channels from ACS.
*/
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE])
mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE]);
if (!IS_DFS_MODE_VALID(mode)) {
hdd_err("attr acs dfs mode is not valid");
return -EINVAL;
}
acs_policy->acs_dfs_mode = mode;
/*
* SCM sends this attribute to provide an active channel,
* to skip redundant ACS between drivers, and save driver start up time
*/
if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT])
channel_hint = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT]);
if (!IS_CHANNEL_VALID(channel_hint)) {
hdd_err("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)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_acs_dfs_mode(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* wlan_hdd_get_sta_roam_dfs_mode() - get sta roam dfs mode policy
* @mode : cfg80211 dfs mode
*
* Return: return csr sta roam dfs mode else return NONE
*/
static enum sta_roam_policy_dfs_mode wlan_hdd_get_sta_roam_dfs_mode(
enum dfs_mode mode)
{
switch (mode) {
case DFS_MODE_ENABLE:
return CSR_STA_ROAM_POLICY_DFS_ENABLED;
case DFS_MODE_DISABLE:
return CSR_STA_ROAM_POLICY_DFS_DISABLED;
case DFS_MODE_DEPRIORITIZE:
return CSR_STA_ROAM_POLICY_DFS_DEPRIORITIZE;
default:
hdd_err("STA Roam policy dfs mode is NONE");
return CSR_STA_ROAM_POLICY_NONE;
}
}
/*
* hdd_get_sap_operating_band: Get current operating channel
* for sap.
* @hdd_ctx: hdd context
*
* Return : Corresponding band for SAP operating channel
*/
uint8_t hdd_get_sap_operating_band(struct hdd_context *hdd_ctx)
{
struct hdd_adapter *adapter;
uint8_t operating_channel = 0;
uint8_t sap_operating_band = 0;
hdd_for_each_adapter(hdd_ctx, adapter) {
if (adapter->device_mode != QDF_SAP_MODE)
continue;
operating_channel = adapter->session.ap.operating_channel;
if (IS_24G_CH(operating_channel))
sap_operating_band = BAND_2G;
else if (IS_5G_CH(operating_channel))
sap_operating_band = BAND_5G;
else
sap_operating_band = BAND_ALL;
}
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;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[
QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX + 1];
int ret;
enum sta_roam_policy_dfs_mode sta_roam_dfs_mode;
enum dfs_mode mode = DFS_MODE_NONE;
bool skip_unsafe_channels = false;
QDF_STATUS status;
uint8_t sap_operating_band;
mac_handle_t mac_handle;
hdd_enter_dev(dev);
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_STA_CONNECT_ROAM_POLICY_MAX,
data, data_len,
wlan_hdd_set_sta_roam_config_policy)) {
hdd_err("invalid attr");
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE])
mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_STA_DFS_MODE]);
if (!IS_DFS_MODE_VALID(mode)) {
hdd_err("attr sta roam dfs mode policy is not valid");
return -EINVAL;
}
sta_roam_dfs_mode = wlan_hdd_get_sta_roam_dfs_mode(mode);
if (tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL])
skip_unsafe_channels = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_STA_SKIP_UNSAFE_CHANNEL]);
sap_operating_band = hdd_get_sap_operating_band(hdd_ctx);
mac_handle = hdd_ctx->mac_handle;
status = sme_update_sta_roam_policy(mac_handle, sta_roam_dfs_mode,
skip_unsafe_channels,
adapter->vdev_id,
sap_operating_band);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_update_sta_roam_policy (err=%d)", status);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_sta_roam_policy() - Wrapper to restrict scan channels,
* connection and roaming for station.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sta_roam_policy will decide if DFS channels or unsafe
* channels needs to be skipped in scanning or not.
* If dfs_mode is disabled, driver will not scan DFS channels.
* If skip_unsafe_channels is set, driver will skip unsafe channels
* in Scanning.
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_sta_roam_policy(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_sta_roam_policy(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifdef FEATURE_WLAN_CH_AVOID
static int hdd_validate_avoid_freq_chanlist(
struct hdd_context *hdd_ctx,
struct ch_avoid_ind_type *channel_list)
{
unsigned int range_idx, ch_idx;
unsigned int unsafe_channel_index, unsafe_channel_count = 0;
bool ch_found = false;
unsafe_channel_count = QDF_MIN((uint16_t)hdd_ctx->unsafe_channel_count,
(uint16_t)NUM_CHANNELS);
for (range_idx = 0; range_idx < channel_list->ch_avoid_range_cnt;
range_idx++) {
if ((channel_list->avoid_freq_range[range_idx].start_freq <
CDS_24_GHZ_CHANNEL_1) ||
(channel_list->avoid_freq_range[range_idx].end_freq >
CDS_5_GHZ_CHANNEL_165) ||
(channel_list->avoid_freq_range[range_idx].start_freq >
channel_list->avoid_freq_range[range_idx].end_freq))
continue;
for (ch_idx = channel_list->
avoid_freq_range[range_idx].start_freq;
ch_idx <= channel_list->
avoid_freq_range[range_idx].end_freq;
ch_idx++) {
if (INVALID_CHANNEL == wlan_reg_get_chan_enum(ch_idx))
continue;
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]) {
hdd_info("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)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int ret;
qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
uint16_t *local_unsafe_list;
uint16_t unsafe_channel_count;
uint16_t unsafe_channel_index, local_unsafe_list_count;
struct ch_avoid_ind_type *channel_list;
enum QDF_GLOBAL_MODE curr_mode;
uint8_t num_args = 0;
bool user_set_avoid_channel = true;
hdd_enter_dev(wdev->netdev);
if (!qdf_ctx) {
hdd_err("qdf_ctx is NULL");
return -EINVAL;
}
curr_mode = hdd_get_conparam();
if (QDF_GLOBAL_FTM_MODE == curr_mode ||
QDF_GLOBAL_MONITOR_MODE == curr_mode) {
hdd_err("Command not allowed in FTM/MONITOR mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (!data && data_len == 0) {
hdd_debug("Userspace doesn't set avoid frequency channel list");
user_set_avoid_channel = false;
goto process_unsafe_channel;
}
if (!data || data_len < (sizeof(channel_list->ch_avoid_range_cnt) +
sizeof(struct ch_avoid_freq_type))) {
hdd_err("Avoid frequency channel list empty");
return -EINVAL;
}
num_args = (data_len - sizeof(channel_list->ch_avoid_range_cnt)) /
sizeof(channel_list->avoid_freq_range[0].start_freq);
if (num_args < 2 || num_args > CH_AVOID_MAX_RANGE * 2 ||
num_args % 2 != 0) {
hdd_err("Invalid avoid frequency channel list");
return -EINVAL;
}
channel_list = (struct ch_avoid_ind_type *)data;
if (channel_list->ch_avoid_range_cnt == 0 ||
channel_list->ch_avoid_range_cnt > CH_AVOID_MAX_RANGE ||
2 * channel_list->ch_avoid_range_cnt != num_args) {
hdd_err("Invalid frequency range count %d",
channel_list->ch_avoid_range_cnt);
return -EINVAL;
}
process_unsafe_channel:
ret = hdd_clone_local_unsafe_chan(hdd_ctx,
&local_unsafe_list,
&local_unsafe_list_count);
if (0 != ret) {
hdd_err("failed to clone the cur unsafe chan list");
return ret;
}
pld_get_wlan_unsafe_channel(qdf_ctx->dev, hdd_ctx->unsafe_channel_list,
&(hdd_ctx->unsafe_channel_count),
sizeof(hdd_ctx->unsafe_channel_list));
if (user_set_avoid_channel) {
hdd_ctx->unsafe_channel_count =
hdd_validate_avoid_freq_chanlist(
hdd_ctx,
channel_list);
unsafe_channel_count = hdd_ctx->unsafe_channel_count;
pld_set_wlan_unsafe_channel(qdf_ctx->dev,
hdd_ctx->unsafe_channel_list,
hdd_ctx->unsafe_channel_count);
} else {
unsafe_channel_count = QDF_MIN(
(uint16_t)hdd_ctx->unsafe_channel_count,
(uint16_t)NUM_CHANNELS);
}
for (unsafe_channel_index = 0;
unsafe_channel_index < unsafe_channel_count;
unsafe_channel_index++) {
hdd_debug("Channel %d is not safe",
hdd_ctx->unsafe_channel_list[unsafe_channel_index]);
}
if (hdd_local_unsafe_channel_updated(hdd_ctx, local_unsafe_list,
local_unsafe_list_count))
hdd_unsafe_channel_restart_sap(hdd_ctx);
qdf_mem_free(local_unsafe_list);
return 0;
}
/**
* wlan_hdd_cfg80211_avoid_freq() - ask driver to restart SAP if SAP
* is on unsafe channel.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* wlan_hdd_cfg80211_avoid_freq do restart the sap if sap is already
* on any of unsafe channels.
* If sap is on any of unsafe channel, hdd_unsafe_channel_restart_sap
* will send WLAN_SVC_LTE_COEX_IND indication to userspace to restart.
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_avoid_freq(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_avoid_freq(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#endif
/**
* __wlan_hdd_cfg80211_sap_configuration_set() - ask driver to restart SAP if
* SAP is on unsafe channel.
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to
* driver.
* QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and
* will initiate restart of sap.
*
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *ndev = wdev->netdev;
struct hdd_adapter *hostapd_adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX + 1];
uint8_t config_channel = 0;
struct hdd_ap_ctx *ap_ctx;
int ret;
QDF_STATUS status;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX,
data, data_len,
wlan_hdd_sap_config_policy)) {
hdd_err("invalid attr");
return -EINVAL;
}
if (tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]) {
if (!test_bit(SOFTAP_BSS_STARTED,
&hostapd_adapter->event_flags)) {
hdd_err("SAP is not started yet. Restart sap will be invalid");
return -EINVAL;
}
config_channel =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL]);
if (!((IS_24G_CH(config_channel)) ||
(IS_5G_CH(config_channel)))) {
hdd_err("Channel %d is not valid to restart SAP",
config_channel);
return -ENOTSUPP;
}
ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter);
ap_ctx->sap_config.channel = config_channel;
ap_ctx->sap_config.ch_params.ch_width =
ap_ctx->sap_config.ch_width_orig;
ap_ctx->bss_stop_reason = BSS_STOP_DUE_TO_VENDOR_CONFIG_CHAN;
wlan_reg_set_channel_params(hdd_ctx->pdev,
ap_ctx->sap_config.channel,
ap_ctx->sap_config.sec_ch,
&ap_ctx->sap_config.ch_params);
hdd_restart_sap(hostapd_adapter);
}
if (tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]) {
uint32_t freq_len, i;
uint32_t *freq;
uint8_t chans[QDF_MAX_NUM_CHAN];
hdd_debug("setting mandatory freq/chan list");
freq_len = nla_len(
tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST])/
sizeof(uint32_t);
if (freq_len > QDF_MAX_NUM_CHAN) {
hdd_err("insufficient space to hold channels");
return -ENOMEM;
}
freq = nla_data(
tb[QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST]);
hdd_debug("freq_len=%d", freq_len);
for (i = 0; i < freq_len; i++) {
chans[i] = ieee80211_frequency_to_channel(freq[i]);
hdd_debug("freq[%d]=%d", i, freq[i]);
}
status = policy_mgr_set_sap_mandatory_channels(
hdd_ctx->psoc, chans, freq_len);
if (QDF_IS_STATUS_ERROR(status))
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_sap_configuration_set() - sap configuration vendor command
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* __wlan_hdd_cfg80211_sap_configuration_set function set SAP params to
* driver.
* QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHAN will set sap config channel and
* will initiate restart of sap.
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_sap_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_sap_configuration_set(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifndef QCA_SUPPORT_CP_STATS
/**
* 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(struct hdd_context *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;
hdd_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) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
hdd_debug("wow_ucast_wake_up_count %d",
data->wow_ucast_wake_up_count);
hdd_debug("wow_bcast_wake_up_count %d",
data->wow_bcast_wake_up_count);
hdd_debug("wow_ipv4_mcast_wake_up_count %d",
data->wow_ipv4_mcast_wake_up_count);
hdd_debug("wow_ipv6_mcast_wake_up_count %d",
data->wow_ipv6_mcast_wake_up_count);
hdd_debug("wow_ipv6_mcast_ra_stats %d",
data->wow_ipv6_mcast_ra_stats);
hdd_debug("wow_ipv6_mcast_ns_stats %d",
data->wow_ipv6_mcast_ns_stats);
hdd_debug("wow_ipv6_mcast_na_stats %d",
data->wow_ipv6_mcast_na_stats);
hdd_debug("wow_icmpv4_count %d", data->wow_icmpv4_count);
hdd_debug("wow_icmpv6_count %d",
data->wow_icmpv6_count);
hdd_debug("wow_rssi_breach_wake_up_count %d",
data->wow_rssi_breach_wake_up_count);
hdd_debug("wow_low_rssi_wake_up_count %d",
data->wow_low_rssi_wake_up_count);
hdd_debug("wow_gscan_wake_up_count %d",
data->wow_gscan_wake_up_count);
hdd_debug("wow_pno_complete_wake_up_count %d",
data->wow_pno_complete_wake_up_count);
hdd_debug("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)) {
hdd_err("nla put fail");
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
hdd_exit();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
#endif
#ifdef QCA_SUPPORT_CP_STATS
static int wlan_hdd_process_wake_lock_stats(struct hdd_context *hdd_ctx)
{
return wlan_cfg80211_mc_cp_stats_get_wakelock_stats(hdd_ctx->psoc,
hdd_ctx->wiphy);
}
#else
/**
* wlan_hdd_process_wake_lock_stats() - wrapper function to absract cp_stats
* or legacy get_wake_lock_stats API.
* @hdd_ctx: pointer to hdd_ctx
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_process_wake_lock_stats(struct hdd_context *hdd_ctx)
{
int ret;
QDF_STATUS qdf_status;
struct sir_wake_lock_stats wake_lock_stats = {0};
qdf_status = wma_get_wakelock_stats(&wake_lock_stats);
if (qdf_status != QDF_STATUS_SUCCESS) {
hdd_err("failed to get wakelock stats(err=%d)", qdf_status);
return -EINVAL;
}
ret = hdd_send_wakelock_stats(hdd_ctx, &wake_lock_stats);
if (ret)
hdd_err("Failed to post wake lock stats");
return ret;
}
#endif
/**
* __wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* WMA copies required data and invokes callback
* wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats.
*
* Return: 0 on success; error number otherwise.
*/
static int __wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return -EINVAL;
ret = wlan_hdd_process_wake_lock_stats(hdd_ctx);
hdd_exit();
return ret;
}
/**
* wlan_hdd_cfg80211_get_wakelock_stats() - gets wake lock stats
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* This function parses the incoming NL vendor command data attributes and
* invokes the SME Api and blocks on a completion variable.
* WMA copies required data and invokes callback
* wlan_hdd_cfg80211_wakelock_stats_rsp_callback to send wake lock stats.
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_cfg80211_get_wakelock_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_wakelock_stats(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_get_bus_size() - Get WMI Bus size
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* This function reads wmi max bus size and fill in the skb with
* NL attributes and send up the NL event.
* Return: 0 on success; errno on failure
*/
static int
__wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int ret_val;
struct sk_buff *skb;
uint32_t nl_buf_len;
hdd_enter();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
hdd_debug("WMI Max Bus size: %d", hdd_ctx->wmi_max_len);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(hdd_ctx->wmi_max_len) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
if (nla_put_u16(skb, QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
hdd_ctx->wmi_max_len)) {
hdd_err("nla put failure");
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
hdd_exit();
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* wlan_hdd_cfg80211_get_bus_size() - SSR Wrapper to Get Bus size
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_get_bus_size(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_bus_size(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
*__wlan_hdd_cfg80211_setband() - set band
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
int ret;
static const struct nla_policy policy[QCA_WLAN_VENDOR_ATTR_MAX + 1]
= {[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = { .type = NLA_U32 } };
hdd_enter();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX,
data, data_len, policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) {
hdd_err("attr SETBAND_VALUE failed");
return -EINVAL;
}
ret = hdd_reg_set_band(dev,
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]));
hdd_exit();
return ret;
}
/**
*wlan_hdd_validate_acs_channel() - validate channel provided by ACS
* @adapter: hdd adapter
* @channel: channel number
*
* return: QDF status based on success or failure
*/
static QDF_STATUS wlan_hdd_validate_acs_channel(struct hdd_adapter *adapter,
int channel, int chan_bw)
{
if (QDF_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(adapter, channel))
return QDF_STATUS_E_FAILURE;
if ((wlansap_is_channel_in_nol_list(WLAN_HDD_GET_SAP_CTX_PTR(adapter),
channel,
PHY_SINGLE_CHANNEL_CENTERED))) {
hdd_info("channel %d is in nol", channel);
return -EINVAL;
}
if ((wlansap_is_channel_leaking_in_nol(
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
channel, chan_bw))) {
hdd_info("channel %d is leaking in nol", channel);
return -EINVAL;
}
return 0;
}
static void hdd_update_acs_sap_config(struct hdd_context *hdd_ctx,
struct sap_config *sap_config,
struct hdd_vendor_chan_info *channel_list)
{
uint8_t ch_width;
QDF_STATUS status;
uint32_t channel_bonding_mode;
sap_config->channel = channel_list->pri_ch;
sap_config->ch_params.center_freq_seg0 =
channel_list->vht_seg0_center_ch;
sap_config->ch_params.center_freq_seg1 =
channel_list->vht_seg1_center_ch;
sap_config->ch_params.sec_ch_offset = channel_list->ht_sec_ch;
sap_config->ch_params.ch_width = channel_list->chan_width;
if (sap_config->channel >= 36) {
status =
ucfg_mlme_get_vht_channel_width(hdd_ctx->psoc,
&ch_width);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("Failed to set channel_width");
sap_config->ch_width_orig = ch_width;
} else {
ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc,
&channel_bonding_mode);
sap_config->ch_width_orig = channel_bonding_mode ?
eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ;
}
sap_config->acs_cfg.pri_ch = channel_list->pri_ch;
sap_config->acs_cfg.ch_width = channel_list->chan_width;
sap_config->acs_cfg.vht_seg0_center_ch =
channel_list->vht_seg0_center_ch;
sap_config->acs_cfg.vht_seg1_center_ch =
channel_list->vht_seg1_center_ch;
sap_config->acs_cfg.ht_sec_ch = channel_list->ht_sec_ch;
}
static int hdd_update_acs_channel(struct hdd_adapter *adapter, uint8_t reason,
uint8_t channel_cnt,
struct hdd_vendor_chan_info *channel_list)
{
struct sap_config *sap_config;
struct hdd_ap_ctx *hdd_ap_ctx;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
QDF_STATUS status = QDF_STATUS_SUCCESS;
mac_handle_t mac_handle;
if (!channel_list) {
hdd_err("channel_list is NULL");
return -EINVAL;
}
hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
sap_config = &adapter->session.ap.sap_config;
if (QDF_TIMER_STATE_RUNNING ==
qdf_mc_timer_get_current_state(&adapter->session.
ap.vendor_acs_timer)) {
qdf_mc_timer_stop(&adapter->session.ap.vendor_acs_timer);
}
if (channel_list->pri_ch == 0) {
/* Check mode, set default channel */
channel_list->pri_ch = 6;
/*
* sap_select_default_oper_chan(mac_handle,
* sap_config->acs_cfg.hw_mode);
*/
}
mac_handle = hdd_ctx->mac_handle;
switch (reason) {
/* SAP init case */
case QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT:
hdd_update_acs_sap_config(hdd_ctx, sap_config, channel_list);
/* Update Hostapd */
wlan_hdd_cfg80211_acs_ch_select_evt(adapter);
break;
/* DFS detected on current channel */
case QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS:
wlan_sap_update_next_channel(
WLAN_HDD_GET_SAP_CTX_PTR(adapter),
channel_list->pri_ch,
channel_list->chan_width);
status = sme_update_new_channel_event(
mac_handle,
adapter->vdev_id);
break;
/* LTE coex event on current channel */
case QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX:
sap_config->acs_cfg.pri_ch = channel_list->pri_ch;
sap_config->acs_cfg.ch_width = channel_list->chan_width;
hdd_ap_ctx->sap_config.ch_width_orig =
channel_list->chan_width;
hdd_switch_sap_channel(adapter, sap_config->acs_cfg.pri_ch,
true);
break;
default:
hdd_info("invalid reason for timer invoke");
}
hdd_exit();
return qdf_status_to_os_return(status);
}
/**
* Define short name for vendor channel set config
*/
#define SET_CHAN_REASON QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
#define SET_CHAN_CHAN_LIST QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST
#define SET_CHAN_PRIMARY_CHANNEL \
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY
#define SET_CHAN_SECONDARY_CHANNEL \
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY
#define SET_CHAN_SEG0_CENTER_CHANNEL \
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0
#define SET_CHAN_SEG1_CENTER_CHANNEL \
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1
#define SET_CHAN_CHANNEL_WIDTH \
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
#define SET_CHAN_MAX QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX
#define SET_EXT_ACS_BAND QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND
static const struct nla_policy acs_chan_config_policy[SET_CHAN_MAX + 1] = {
[SET_CHAN_REASON] = {.type = NLA_U8},
[SET_CHAN_CHAN_LIST] = {.type = NLA_NESTED},
};
static const struct nla_policy acs_chan_list_policy[SET_CHAN_MAX + 1] = {
[SET_CHAN_PRIMARY_CHANNEL] = {.type = NLA_U8},
[SET_CHAN_SECONDARY_CHANNEL] = {.type = NLA_U8},
[SET_CHAN_SEG0_CENTER_CHANNEL] = {.type = NLA_U8},
[SET_CHAN_SEG1_CENTER_CHANNEL] = {.type = NLA_U8},
[SET_CHAN_CHANNEL_WIDTH] = {.type = NLA_U8},
[SET_EXT_ACS_BAND] = {.type = NLA_U8},
};
/**
* hdd_parse_vendor_acs_chan_config() - API to parse vendor acs channel config
* @channel_list: pointer to hdd_vendor_chan_info
* @reason: channel change reason
* @channel_cnt: channel count
* @data: data
* @data_len: data len
*
* Return: 0 on success, negative errno on failure
*/
static int hdd_parse_vendor_acs_chan_config(struct hdd_vendor_chan_info
**chan_list_ptr, uint8_t *reason, uint8_t *channel_cnt,
const void *data, int data_len)
{
int rem;
uint32_t i = 0;
struct nlattr *tb[SET_CHAN_MAX + 1];
struct nlattr *tb2[SET_CHAN_MAX + 1];
struct nlattr *curr_attr;
struct hdd_vendor_chan_info *channel_list;
if (wlan_cfg80211_nla_parse(tb, SET_CHAN_MAX, data, data_len,
acs_chan_config_policy)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (tb[SET_CHAN_REASON])
*reason = nla_get_u8(tb[SET_CHAN_REASON]);
if (!tb[SET_CHAN_CHAN_LIST]) {
hdd_err("channel list empty");
return -EINVAL;
}
nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem)
i++;
if (i > MAX_CHANNEL) {
hdd_err("Error: Exceeded max channels: %u", MAX_CHANNEL);
return -ENOMEM;
}
*channel_cnt = (uint8_t)i;
if (i == 0)
hdd_err("incorrect channel count");
channel_list = qdf_mem_malloc(sizeof(struct hdd_vendor_chan_info) *
(*channel_cnt));
if (!channel_list)
return -ENOMEM;
i = 0;
nla_for_each_nested(curr_attr, tb[SET_CHAN_CHAN_LIST], rem) {
if (wlan_cfg80211_nla_parse_nested(tb2, SET_CHAN_MAX,
curr_attr,
acs_chan_list_policy)) {
hdd_err("nla_parse failed");
qdf_mem_free(channel_list);
return -EINVAL;
}
if (tb2[SET_EXT_ACS_BAND]) {
channel_list[i].band =
nla_get_u8(tb2[SET_EXT_ACS_BAND]);
}
/* Parse and Fetch allowed SSID list*/
if (tb2[SET_CHAN_PRIMARY_CHANNEL]) {
channel_list[i].pri_ch =
nla_get_u8(
tb2[SET_CHAN_PRIMARY_CHANNEL]);
}
if (tb2[SET_CHAN_SECONDARY_CHANNEL]) {
channel_list[i].ht_sec_ch =
nla_get_u8(tb2[SET_CHAN_SECONDARY_CHANNEL]);
}
if (tb2[SET_CHAN_SEG0_CENTER_CHANNEL]) {
channel_list[i].vht_seg0_center_ch =
nla_get_u8(tb2[SET_CHAN_SEG0_CENTER_CHANNEL]);
}
if (tb2[SET_CHAN_SEG1_CENTER_CHANNEL]) {
channel_list[i].vht_seg1_center_ch =
nla_get_u8(tb2[SET_CHAN_SEG1_CENTER_CHANNEL]);
}
if (tb2[SET_CHAN_CHANNEL_WIDTH]) {
channel_list[i].chan_width =
nla_get_u8(tb2[SET_CHAN_CHANNEL_WIDTH]);
}
hdd_debug("index %d pri %d sec %d seg0 %d seg1 %d width %d",
i, channel_list[i].pri_ch,
channel_list[i].ht_sec_ch,
channel_list[i].vht_seg0_center_ch,
channel_list[i].vht_seg1_center_ch,
channel_list[i].chan_width);
i++;
}
*chan_list_ptr = channel_list;
return 0;
}
/**
* Undef short names for vendor set channel configuration
*/
#undef SET_CHAN_REASON
#undef SET_CHAN_CHAN_LIST
#undef SET_CHAN_PRIMARY_CHANNEL
#undef SET_CHAN_SECONDARY_CHANNEL
#undef SET_CHAN_SEG0_CENTER_CHANNEL
#undef SET_CHAN_SEG1_CENTER_CHANNEL
#undef SET_CHAN_CHANNEL_WIDTH
#undef SET_CHAN_MAX
/**
* __wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret_val;
QDF_STATUS qdf_status;
uint8_t channel_cnt = 0, reason = -1;
struct hdd_vendor_chan_info *channel_list = NULL;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct hdd_vendor_chan_info *channel_list_ptr;
hdd_enter();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (test_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags))
clear_bit(VENDOR_ACS_RESPONSE_PENDING, &adapter->event_flags);
else {
hdd_err("already timeout happened for acs");
return -EINVAL;
}
ret_val = hdd_parse_vendor_acs_chan_config(&channel_list, &reason,
&channel_cnt, data, data_len);
channel_list_ptr = channel_list;
if (ret_val)
return ret_val;
/* Validate channel to be set */
while (channel_cnt && channel_list) {
qdf_status = wlan_hdd_validate_acs_channel(adapter,
channel_list->pri_ch,
channel_list->chan_width);
if (qdf_status == QDF_STATUS_SUCCESS)
break;
else if (channel_cnt == 1) {
hdd_err("invalid channel %d received from app",
channel_list->pri_ch);
channel_list->pri_ch = 0;
break;
}
channel_cnt--;
channel_list++;
}
if ((channel_cnt <= 0) || !channel_list) {
hdd_err("no available channel/chanlist %d/%pK", channel_cnt,
channel_list);
qdf_mem_free(channel_list_ptr);
return -EINVAL;
}
hdd_debug("received primary channel as %d", channel_list->pri_ch);
ret_val = hdd_update_acs_channel(adapter, reason,
channel_cnt, channel_list);
qdf_mem_free(channel_list_ptr);
return ret_val;
}
/**
* wlan_hdd_cfg80211_update_vendor_channel() - update vendor channel
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_update_vendor_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_update_vendor_channel(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_cfg80211_setband() - Wrapper to setband
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_setband(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_cfg80211_sar_convert_limit_set() - Convert limit set value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR limit set to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_limit_set(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE:
*wmi_value = WMI_SAR_FEATURE_OFF;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0:
*wmi_value = WMI_SAR_FEATURE_ON_SET_0;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1:
*wmi_value = WMI_SAR_FEATURE_ON_SET_1;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2:
*wmi_value = WMI_SAR_FEATURE_ON_SET_2;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3:
*wmi_value = WMI_SAR_FEATURE_ON_SET_3;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4:
*wmi_value = WMI_SAR_FEATURE_ON_SET_4;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER:
*wmi_value = WMI_SAR_FEATURE_ON_USER_DEFINED;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0:
*wmi_value = WMI_SAR_FEATURE_ON_SAR_V2_0;
break;
default:
ret = -1;
}
return ret;
}
#ifdef WLAN_FEATURE_SARV1_TO_SARV2
/**
* hdd_convert_sarv1_to_sarv2() - convert SAR V1 BDF reference to SAR V2
* @hdd_ctx: The HDD global context
* @tb: The parsed array of netlink attributes
* @sar_limit_cmd: The WMI command to be filled
*
* This feature/function is designed to solve the following problem:
* 1) Userspace application was written to use SARv1 BDF entries
* 2) Product is configured with SAR V2 BDF entries
*
* So if this feature is enabled, and if the firmware is configured
* with SAR V2 support, and if the incoming request is to enable a SAR
* V1 BDF entry, then the WMI command is generated to actually
* configure a SAR V2 BDF entry.
*
* Return: true if conversion was performed and @sar_limit_cmd is
* ready to be sent to firmware. Otherwise false in which case the
* normal parsing logic should be applied.
*/
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
struct nlattr *attr;
uint32_t bdf_index, set;
struct sar_limit_cmd_row *row;
if (hdd_ctx->sar_version != SAR_VERSION_2) {
hdd_debug("SAR version: %d", hdd_ctx->sar_version);
return false;
}
attr = tb[QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE];
if (!attr)
return false;
bdf_index = nla_get_u32(attr);
if ((bdf_index >= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0) &&
(bdf_index <= QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4)) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0;
} else if (bdf_index == QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE) {
set = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE;
bdf_index = QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0;
} else {
return false;
}
/* Need two rows to hold the per-chain V2 power index
* To disable SARv2 limit, send chain, num_limits_row and
* power limit set to 0 (except power index 0xff)
*/
row = qdf_mem_malloc(2 * sizeof(*row));
if (!row)
return false;
if (wlan_hdd_cfg80211_sar_convert_limit_set(
set, &sar_limit_cmd->sar_enable)) {
hdd_err("Failed to convert SAR limit to WMI value");
return false;
}
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->num_limit_rows = 2;
sar_limit_cmd->sar_limit_row_list = row;
row[0].limit_value = bdf_index;
row[1].limit_value = row[0].limit_value;
row[0].chain_id = 0;
row[1].chain_id = 1;
row[0].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
row[1].validity_bitmap = WMI_SAR_CHAIN_ID_VALID_MASK;
return true;
}
#else /* WLAN_FEATURE_SARV1_TO_SARV2 */
static bool
hdd_convert_sarv1_to_sarv2(struct hdd_context *hdd_ctx,
struct nlattr *tb[],
struct sar_limit_cmd_params *sar_limit_cmd)
{
return false;
}
#endif /* WLAN_FEATURE_SARV1_TO_SARV2 */
/**
* wlan_hdd_cfg80211_sar_convert_band() - Convert WLAN band value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR BAND to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_band(u32 nl80211_value, u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case HDD_NL80211_BAND_2GHZ:
*wmi_value = WMI_SAR_2G_ID;
break;
case HDD_NL80211_BAND_5GHZ:
*wmi_value = WMI_SAR_5G_ID;
break;
default:
ret = -1;
}
return ret;
}
/**
* wlan_hdd_cfg80211_sar_convert_modulation() - Convert WLAN modulation value
* @nl80211_value: Vendor command attribute value
* @wmi_value: Pointer to return converted WMI return value
*
* Convert NL80211 vendor command value for SAR Modulation to WMI value
* Return: 0 on success, -1 on invalid value
*/
static int wlan_hdd_cfg80211_sar_convert_modulation(u32 nl80211_value,
u32 *wmi_value)
{
int ret = 0;
switch (nl80211_value) {
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK:
*wmi_value = WMI_SAR_MOD_CCK;
break;
case QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM:
*wmi_value = WMI_SAR_MOD_OFDM;
break;
default:
ret = -1;
}
return ret;
}
void hdd_store_sar_config(struct hdd_context *hdd_ctx,
struct sar_limit_cmd_params *sar_limit_cmd)
{
/* Free the previously stored sar_limit_cmd */
wlan_hdd_free_sar_config(hdd_ctx);
hdd_ctx->sar_cmd_params = sar_limit_cmd;
}
void wlan_hdd_free_sar_config(struct hdd_context *hdd_ctx)
{
struct sar_limit_cmd_params *sar_limit_cmd;
if (!hdd_ctx->sar_cmd_params)
return;
sar_limit_cmd = hdd_ctx->sar_cmd_params;
hdd_ctx->sar_cmd_params = NULL;
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
#define SAR_LIMITS_SAR_ENABLE QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE
#define SAR_LIMITS_NUM_SPECS QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS
#define SAR_LIMITS_SPEC QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC
#define SAR_LIMITS_SPEC_BAND QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND
#define SAR_LIMITS_SPEC_CHAIN QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN
#define SAR_LIMITS_SPEC_MODULATION \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION
#define SAR_LIMITS_SPEC_POWER_LIMIT \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT
#define SAR_LIMITS_SPEC_POWER_LIMIT_INDEX \
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#define SAR_LIMITS_MAX QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX
static const struct nla_policy
sar_limits_policy[SAR_LIMITS_MAX + 1] = {
[SAR_LIMITS_SAR_ENABLE] = {.type = NLA_U32},
[SAR_LIMITS_NUM_SPECS] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_BAND] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_CHAIN] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_MODULATION] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT] = {.type = NLA_U32},
[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX] = {.type = NLA_U32},
};
/**
* hdd_extract_sar_nested_attrs() - Extract nested SAR attribute
* @spec: nested nla attribue
* @row: output to hold extract nested attribute
*
* This function extracts nested SAR attribute one at a time which means
* for each nested attribute this has to be invoked from
* __wlan_hdd_set_sar_power_limits().
*
* Return: On success - 0
* On Failure - Negative value
*/
static int hdd_extract_sar_nested_attrs(struct nlattr *spec[],
struct sar_limit_cmd_row *row)
{
uint32_t limit;
uint32_t band;
uint32_t modulation;
int ret;
row->validity_bitmap = 0;
if (spec[SAR_LIMITS_SPEC_POWER_LIMIT]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT]);
row->limit_value = limit;
} else if (spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]) {
limit = nla_get_u32(spec[SAR_LIMITS_SPEC_POWER_LIMIT_INDEX]);
row->limit_value = limit;
} else {
hdd_err("SAR Spec does not have power limit or index value");
return -EINVAL;
}
if (spec[SAR_LIMITS_SPEC_BAND]) {
band = nla_get_u32(spec[SAR_LIMITS_SPEC_BAND]);
ret = wlan_hdd_cfg80211_sar_convert_band(band, &row->band_id);
if (ret) {
hdd_err("Invalid SAR Band attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_BAND_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_CHAIN]) {
row->chain_id = nla_get_u32(spec[SAR_LIMITS_SPEC_CHAIN]);
row->validity_bitmap |= WMI_SAR_CHAIN_ID_VALID_MASK;
}
if (spec[SAR_LIMITS_SPEC_MODULATION]) {
modulation = nla_get_u32(spec[SAR_LIMITS_SPEC_MODULATION]);
ret = wlan_hdd_cfg80211_sar_convert_modulation(modulation,
&row->mod_id);
if (ret) {
hdd_err("Invalid SAR Modulation attr");
return ret;
}
row->validity_bitmap |= WMI_SAR_MOD_ID_VALID_MASK;
}
return 0;
}
/**
* __wlan_hdd_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to setup Specific Absorption Rate limit specs.
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *spec[SAR_LIMITS_MAX + 1];
struct nlattr *tb[SAR_LIMITS_MAX + 1];
struct nlattr *spec_list;
struct sar_limit_cmd_params *sar_limit_cmd;
int ret = -EINVAL, i = 0, rem = 0;
QDF_STATUS status;
uint32_t num_limit_rows = 0;
struct sar_limit_cmd_row *row;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (wlan_cfg80211_nla_parse(tb, SAR_LIMITS_MAX, data, data_len,
sar_limits_policy)) {
hdd_err("Invalid SAR attributes");
return -EINVAL;
}
sar_limit_cmd = qdf_mem_malloc(sizeof(struct sar_limit_cmd_params));
if (!sar_limit_cmd)
return -ENOMEM;
/* is special SAR V1 => SAR V2 logic enabled and applicable? */
if (hdd_ctx->config->sar_version == 2 &&
(hdd_convert_sarv1_to_sarv2(hdd_ctx, tb, sar_limit_cmd)))
goto send_sar_limits;
/* Vendor command manadates all SAR Specs in single call */
sar_limit_cmd->commit_limits = 1;
sar_limit_cmd->sar_enable = WMI_SAR_FEATURE_NO_CHANGE;
if (tb[SAR_LIMITS_SAR_ENABLE]) {
uint32_t sar_enable = nla_get_u32(tb[SAR_LIMITS_SAR_ENABLE]);
uint32_t *sar_ptr = &sar_limit_cmd->sar_enable;
ret = wlan_hdd_cfg80211_sar_convert_limit_set(sar_enable,
sar_ptr);
if (ret) {
hdd_err("Invalid SAR Enable attr");
goto fail;
}
}
hdd_debug("attr sar sar_enable %d", sar_limit_cmd->sar_enable);
if (tb[SAR_LIMITS_NUM_SPECS]) {
num_limit_rows = nla_get_u32(tb[SAR_LIMITS_NUM_SPECS]);
hdd_debug("attr sar num_limit_rows %u", num_limit_rows);
}
if (num_limit_rows > MAX_SAR_LIMIT_ROWS_SUPPORTED) {
hdd_err("SAR Spec list exceed supported size");
goto fail;
}
if (num_limit_rows == 0)
goto send_sar_limits;
row = qdf_mem_malloc(sizeof(*row) * num_limit_rows);
if (!row) {
hdd_err("Failed to allocate memory for sar_limit_row_list");
goto fail;
}
sar_limit_cmd->num_limit_rows = num_limit_rows;
sar_limit_cmd->sar_limit_row_list = row;
if (!tb[SAR_LIMITS_SPEC]) {
hdd_err("Invalid SAR specification list");
goto fail;
}
nla_for_each_nested(spec_list, tb[SAR_LIMITS_SPEC], rem) {
if (i == num_limit_rows) {
hdd_warn("SAR Cmd has excess SPECs in list");
break;
}
if (wlan_cfg80211_nla_parse(spec,
SAR_LIMITS_MAX,
nla_data(spec_list),
nla_len(spec_list),
sar_limits_policy)) {
hdd_err("nla_parse failed for SAR Spec list");
goto fail;
}
ret = hdd_extract_sar_nested_attrs(spec, row);
if (ret) {
hdd_err("Failed to extract SAR nested attrs");
goto fail;
}
hdd_debug("Spec_ID: %d, Band: %d Chain: %d Mod: %d POW_Limit: %d Validity_Bitmap: %d",
i, row->band_id, row->chain_id, row->mod_id,
row->limit_value, row->validity_bitmap);
i++;
row++;
}
if (i < sar_limit_cmd->num_limit_rows) {
hdd_warn("SAR Cmd has less SPECs in list");
sar_limit_cmd->num_limit_rows = i;
}
send_sar_limits:
status = sme_set_sar_power_limits(hdd_ctx->mac_handle, sar_limit_cmd);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to set sar power limits");
goto fail;
}
/* After SSR, the SAR configuration is lost. As SSR is hidden from
* userland, this command will not come from userspace after a SSR. To
* restore this configuration, save this in hdd context and restore
* after re-init.
*/
hdd_store_sar_config(hdd_ctx, sar_limit_cmd);
return 0;
fail:
if (sar_limit_cmd) {
qdf_mem_free(sar_limit_cmd->sar_limit_row_list);
qdf_mem_free(sar_limit_cmd);
}
return ret;
}
#undef SAR_LIMITS_SAR_ENABLE
#undef SAR_LIMITS_NUM_SPECS
#undef SAR_LIMITS_SPEC
#undef SAR_LIMITS_SPEC_BAND
#undef SAR_LIMITS_SPEC_CHAIN
#undef SAR_LIMITS_SPEC_MODULATION
#undef SAR_LIMITS_SPEC_POWER_LIMIT
#undef SAR_LIMITS_SPEC_POWER_LIMIT_INDEX
#undef SAR_LIMITS_MAX
/**
* wlan_hdd_cfg80211_set_sar_power_limits() - Set SAR power limits
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_set_sar_power_limits()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_set_sar_power_limits(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_set_sar_power_limits(wiphy, wdev, data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
static const struct
nla_policy qca_wlan_vendor_attr[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY] = {.type = NLA_U32},
[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {.type = NLA_BINARY,
.len = QDF_MAC_ADDR_SIZE},
};
void wlan_hdd_rso_cmd_status_cb(hdd_handle_t hdd_handle,
struct rso_cmd_status *rso_status)
{
struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle);
struct hdd_adapter *adapter;
adapter = hdd_get_adapter_by_vdev(hdd_ctx, rso_status->vdev_id);
if (!adapter) {
hdd_err("adapter NULL");
return;
}
adapter->lfr_fw_status.is_disabled = rso_status->status;
complete(&adapter->lfr_fw_status.disable_lfr_event);
}
/**
* __wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* This function is used to enable/disable roaming using vendor commands
*
* Return: 0 on success, negative errno on failure
*/
static int __wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
uint32_t is_fast_roam_enabled;
int ret;
QDF_STATUS qdf_status;
unsigned long rc;
struct hdd_station_ctx *hdd_sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
mac_handle_t mac_handle;
hdd_enter_dev(dev);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_cfg80211_nla_parse(tb,
QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
qca_wlan_vendor_attr);
if (ret) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
/* Parse and fetch Enable flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) {
hdd_err("attr enable failed");
return -EINVAL;
}
is_fast_roam_enabled = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]);
hdd_debug("isFastRoamEnabled %d", is_fast_roam_enabled);
/* Update roaming */
mac_handle = hdd_ctx->mac_handle;
qdf_status = sme_config_fast_roaming(mac_handle, adapter->vdev_id,
is_fast_roam_enabled);
if (qdf_status != QDF_STATUS_SUCCESS)
hdd_err("sme_config_fast_roaming failed with status=%d",
qdf_status);
ret = qdf_status_to_os_return(qdf_status);
if (eConnectionState_Associated == hdd_sta_ctx->conn_info.conn_state &&
QDF_IS_STATUS_SUCCESS(qdf_status) && !is_fast_roam_enabled) {
INIT_COMPLETION(adapter->lfr_fw_status.disable_lfr_event);
/*
* wait only for LFR disable in fw as LFR enable
* is always success
*/
rc = wait_for_completion_timeout(
&adapter->lfr_fw_status.disable_lfr_event,
msecs_to_jiffies(WAIT_TIME_RSO_CMD_STATUS));
if (!rc) {
hdd_err("Timed out waiting for RSO CMD status");
return -ETIMEDOUT;
}
if (!adapter->lfr_fw_status.is_disabled) {
hdd_err("Roam disable attempt in FW fails");
return -EBUSY;
}
}
hdd_exit();
return ret;
}
/**
* wlan_hdd_cfg80211_set_fast_roaming() - enable/disable roaming
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_set_fast_roaming()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_fast_roaming(wiphy, wdev,
data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_setarp_stats_cmd()
*/
#define STATS_SET_INVALID \
QCA_ATTR_NUD_STATS_SET_INVALID
#define STATS_SET_START \
QCA_ATTR_NUD_STATS_SET_START
#define STATS_GW_IPV4 \
QCA_ATTR_NUD_STATS_GW_IPV4
#define STATS_SET_DATA_PKT_INFO \
QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO
#define STATS_SET_MAX \
QCA_ATTR_NUD_STATS_SET_MAX
const struct nla_policy
qca_wlan_vendor_set_nud_stats[STATS_SET_MAX + 1] = {
[STATS_SET_START] = {.type = NLA_FLAG },
[STATS_GW_IPV4] = {.type = NLA_U32 },
[STATS_SET_DATA_PKT_INFO] = {.type = NLA_U32 },
};
/* define short names for the global vendor params */
#define CONNECTIVITY_STATS_SET_INVALID \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_INVALID
#define STATS_PKT_INFO_TYPE \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_STATS_PKT_INFO_TYPE
#define STATS_DNS_DOMAIN_NAME \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_DNS_DOMAIN_NAME
#define STATS_SRC_PORT \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_SRC_PORT
#define STATS_DEST_PORT \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_PORT
#define STATS_DEST_IPV4 \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV4
#define STATS_DEST_IPV6 \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV6
#define CONNECTIVITY_STATS_SET_MAX \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_MAX
const struct nla_policy
qca_wlan_vendor_set_connectivity_check_stats[CONNECTIVITY_STATS_SET_MAX + 1] = {
[STATS_PKT_INFO_TYPE] = {.type = NLA_U32 },
[STATS_DNS_DOMAIN_NAME] = {.type = NLA_NUL_STRING,
.len = DNS_DOMAIN_NAME_MAX_LEN },
[STATS_SRC_PORT] = {.type = NLA_U32 },
[STATS_DEST_PORT] = {.type = NLA_U32 },
[STATS_DEST_IPV4] = {.type = NLA_U32 },
[STATS_DEST_IPV6] = {.type = NLA_BINARY,
.len = ICMPv6_ADDR_LEN },
};
/**
* hdd_dns_unmake_name_query() - Convert an uncompressed DNS name to a
* NUL-terminated string
* @name: DNS name
*
* Return: Produce a printable version of a DNS name.
*/
static inline uint8_t *hdd_dns_unmake_name_query(uint8_t *name)
{
uint8_t *p;
unsigned int len;
p = name;
while ((len = *p)) {
*(p++) = '.';
p += len;
}
return name + 1;
}
/**
* hdd_dns_make_name_query() - Convert a standard NUL-terminated string
* to DNS name
* @string: Name as a NUL-terminated string
* @buf: Buffer in which to place DNS name
*
* DNS names consist of "<length>element" pairs.
*
* Return: Byte following constructed DNS name
*/
static uint8_t *hdd_dns_make_name_query(const uint8_t *string,
uint8_t *buf, uint8_t len)
{
uint8_t *length_byte = buf++;
uint8_t c;
if (string[len - 1]) {
hdd_debug("DNS name is not null terminated");
return NULL;
}
while ((c = *(string++))) {
if (c == '.') {
*length_byte = buf - length_byte - 1;
length_byte = buf;
}
*(buf++) = c;
}
*length_byte = buf - length_byte - 1;
*(buf++) = '\0';
return buf;
}
/**
* hdd_set_clear_connectivity_check_stats_info() - set/clear stats info
* @adapter: Pointer to hdd adapter
* @arp_stats_params: arp stats structure to be sent to FW
* @tb: nl attribute
* @is_set_stats: set/clear stats
*
*
* Return: 0 on success, negative errno on failure
*/
static int hdd_set_clear_connectivity_check_stats_info(
struct hdd_adapter *adapter,
struct set_arp_stats_params *arp_stats_params,
struct nlattr **tb, bool is_set_stats)
{
struct nlattr *tb2[CONNECTIVITY_STATS_SET_MAX + 1];
struct nlattr *curr_attr = NULL;
int err = 0;
uint32_t pkt_bitmap;
int rem;
/* Set NUD command for start tracking is received. */
nla_for_each_nested(curr_attr,
tb[STATS_SET_DATA_PKT_INFO],
rem) {
if (wlan_cfg80211_nla_parse(tb2,
CONNECTIVITY_STATS_SET_MAX,
nla_data(curr_attr), nla_len(curr_attr),
qca_wlan_vendor_set_connectivity_check_stats)) {
hdd_err("nla_parse failed");
err = -EINVAL;
goto end;
}
if (tb2[STATS_PKT_INFO_TYPE]) {
pkt_bitmap = nla_get_u32(tb2[STATS_PKT_INFO_TYPE]);
if (!pkt_bitmap) {
hdd_err("pkt tracking bitmap is empty");
err = -EINVAL;
goto end;
}
if (is_set_stats) {
arp_stats_params->pkt_type_bitmap = pkt_bitmap;
arp_stats_params->flag = true;
adapter->pkt_type_bitmap |=
arp_stats_params->pkt_type_bitmap;
if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) {
if (!tb[STATS_GW_IPV4]) {
hdd_err("GW ipv4 address is not present");
err = -EINVAL;
goto end;
}
arp_stats_params->ip_addr =
nla_get_u32(tb[STATS_GW_IPV4]);
arp_stats_params->pkt_type =
WLAN_NUD_STATS_ARP_PKT_TYPE;
adapter->track_arp_ip =
arp_stats_params->ip_addr;
}
if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) {
uint8_t *domain_name;
if (!tb2[STATS_DNS_DOMAIN_NAME]) {
hdd_err("DNS domain id is not present");
err = -EINVAL;
goto end;
}
domain_name = nla_data(
tb2[STATS_DNS_DOMAIN_NAME]);
adapter->track_dns_domain_len =
nla_len(tb2[
STATS_DNS_DOMAIN_NAME]);
if (!hdd_dns_make_name_query(
domain_name,
adapter->dns_payload,
adapter->track_dns_domain_len))
adapter->track_dns_domain_len =
0;
/* DNStracking isn't supported in FW. */
arp_stats_params->pkt_type_bitmap &=
~CONNECTIVITY_CHECK_SET_DNS;
}
if (pkt_bitmap &
CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) {
if (!tb2[STATS_SRC_PORT] ||
!tb2[STATS_DEST_PORT]) {
hdd_err("Source/Dest port is not present");
err = -EINVAL;
goto end;
}
arp_stats_params->tcp_src_port =
nla_get_u32(
tb2[STATS_SRC_PORT]);
arp_stats_params->tcp_dst_port =
nla_get_u32(
tb2[STATS_DEST_PORT]);
adapter->track_src_port =
arp_stats_params->tcp_src_port;
adapter->track_dest_port =
arp_stats_params->tcp_dst_port;
}
if (pkt_bitmap &
CONNECTIVITY_CHECK_SET_ICMPV4) {
if (!tb2[STATS_DEST_IPV4]) {
hdd_err("destination ipv4 address to track ping packets is not present");
err = -EINVAL;
goto end;
}
arp_stats_params->icmp_ipv4 =
nla_get_u32(
tb2[STATS_DEST_IPV4]);
adapter->track_dest_ipv4 =
arp_stats_params->icmp_ipv4;
}
} else {
/* clear stats command received */
arp_stats_params->pkt_type_bitmap = pkt_bitmap;
arp_stats_params->flag = false;
adapter->pkt_type_bitmap &=
(~arp_stats_params->pkt_type_bitmap);
if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) {
arp_stats_params->pkt_type =
WLAN_NUD_STATS_ARP_PKT_TYPE;
qdf_mem_zero(&adapter->hdd_stats.
hdd_arp_stats,
sizeof(adapter->hdd_stats.
hdd_arp_stats));
adapter->track_arp_ip = 0;
}
if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) {
/* DNStracking isn't supported in FW. */
arp_stats_params->pkt_type_bitmap &=
~CONNECTIVITY_CHECK_SET_DNS;
qdf_mem_zero(&adapter->hdd_stats.
hdd_dns_stats,
sizeof(adapter->hdd_stats.
hdd_dns_stats));
qdf_mem_zero(adapter->dns_payload,
adapter->track_dns_domain_len);
adapter->track_dns_domain_len = 0;
}
if (pkt_bitmap &
CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) {
qdf_mem_zero(&adapter->hdd_stats.
hdd_tcp_stats,
sizeof(adapter->hdd_stats.
hdd_tcp_stats));
adapter->track_src_port = 0;
adapter->track_dest_port = 0;
}
if (pkt_bitmap &
CONNECTIVITY_CHECK_SET_ICMPV4) {
qdf_mem_zero(&adapter->hdd_stats.
hdd_icmpv4_stats,
sizeof(adapter->hdd_stats.
hdd_icmpv4_stats));
adapter->track_dest_ipv4 = 0;
}
}
} else {
hdd_err("stats list empty");
err = -EINVAL;
goto end;
}
}
end:
return err;
}
void hdd_update_cca_info_cb(hdd_handle_t hdd_handle, uint32_t congestion,
uint32_t vdev_id)
{
struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle);
int status;
struct hdd_adapter *adapter = NULL;
struct hdd_station_ctx *hdd_sta_ctx;
status = wlan_hdd_validate_context(hdd_ctx);
if (status != 0)
return;
adapter = hdd_get_adapter_by_vdev(hdd_ctx, vdev_id);
if (!adapter) {
hdd_err("vdev_id %d does not exist with host", vdev_id);
return;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
hdd_sta_ctx->conn_info.cca = congestion;
hdd_info("congestion:%d", congestion);
}
static const struct nla_policy qca_wlan_vendor_set_trace_level_policy[
QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM] = {.type = NLA_NESTED },
[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK] = {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_set_trace_level() - Set the trace level
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1];
struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX + 1];
struct nlattr *apth;
int rem;
int ret = 1;
int print_idx = -1;
int module_id = -1;
int bit_mask = -1;
int status;
hdd_enter();
if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret != 0)
return -EINVAL;
print_idx = qdf_get_pidx();
if (print_idx < 0 || print_idx >= MAX_PRINT_CONFIG_SUPPORTED) {
hdd_err("Invalid print controle object index");
return -EINVAL;
}
if (wlan_cfg80211_nla_parse(tb1,
QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX,
data, data_len,
qca_wlan_vendor_set_trace_level_policy)) {
hdd_err("Invalid attr");
return -EINVAL;
}
if (!tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM]) {
hdd_err("attr trace level param failed");
return -EINVAL;
}
nla_for_each_nested(apth,
tb1[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_PARAM], rem) {
if (wlan_cfg80211_nla_parse(tb2,
QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MAX,
nla_data(apth), nla_len(apth),
qca_wlan_vendor_set_trace_level_policy)) {
hdd_err("Invalid attr");
return -EINVAL;
}
if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]) {
hdd_err("attr Module ID failed");
return -EINVAL;
}
module_id = nla_get_u32
(tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_MODULE_ID]);
if (!tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]) {
hdd_err("attr Verbose mask failed");
return -EINVAL;
}
bit_mask = nla_get_u32
(tb2[QCA_WLAN_VENDOR_ATTR_SET_TRACE_LEVEL_TRACE_MASK]);
status = hdd_qdf_trace_enable(module_id, bit_mask);
if (status != 0)
hdd_err("can not set verbose mask %d for the category %d",
bit_mask, module_id);
}
hdd_exit();
return ret;
}
/**
* wlan_hdd_cfg80211_set_trace_level() - Set the trace level
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Length of @data
*
* Wrapper function of __wlan_hdd_cfg80211_set_trace_level()
*
* Return: 0 on success, negative errno on failure
*/
static int wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_trace_level(wiphy, wdev,
data, data_len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to send arp stats to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct nlattr *tb[STATS_SET_MAX + 1];
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct set_arp_stats_params arp_stats_params = {0};
int err = 0;
mac_handle_t mac_handle;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
err = wlan_hdd_validate_context(hdd_ctx);
if (0 != err)
return err;
if (adapter->vdev_id == WLAN_UMAC_VDEV_ID_MAX) {
hdd_err("Invalid vdev id");
return -EINVAL;
}
if (adapter->device_mode != QDF_STA_MODE) {
hdd_err("STATS supported in only STA mode!");
return -EINVAL;
}
err = wlan_cfg80211_nla_parse(tb, STATS_SET_MAX, data, data_len,
qca_wlan_vendor_set_nud_stats);
if (err) {
hdd_err("STATS_SET_START ATTR");
return err;
}
if (tb[STATS_SET_START]) {
/* tracking is enabled for stats other than arp. */
if (tb[STATS_SET_DATA_PKT_INFO]) {
err = hdd_set_clear_connectivity_check_stats_info(
adapter,
&arp_stats_params, tb, true);
if (err)
return -EINVAL;
/*
* if only tracking dns, then don't send
* wmi command to FW.
*/
if (!arp_stats_params.pkt_type_bitmap)
return err;
} else {
if (!tb[STATS_GW_IPV4]) {
hdd_err("STATS_SET_START CMD");
return -EINVAL;
}
arp_stats_params.pkt_type_bitmap =
CONNECTIVITY_CHECK_SET_ARP;
adapter->pkt_type_bitmap |=
arp_stats_params.pkt_type_bitmap;
arp_stats_params.flag = true;
arp_stats_params.ip_addr =
nla_get_u32(tb[STATS_GW_IPV4]);
adapter->track_arp_ip = arp_stats_params.ip_addr;
arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
}
} else {
/* clear stats command received. */
if (tb[STATS_SET_DATA_PKT_INFO]) {
err = hdd_set_clear_connectivity_check_stats_info(
adapter,
&arp_stats_params, tb, false);
if (err)
return -EINVAL;
/*
* if only tracking dns, then don't send
* wmi command to FW.
*/
if (!arp_stats_params.pkt_type_bitmap)
return err;
} else {
arp_stats_params.pkt_type_bitmap =
CONNECTIVITY_CHECK_SET_ARP;
adapter->pkt_type_bitmap &=
(~arp_stats_params.pkt_type_bitmap);
arp_stats_params.flag = false;
qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
sizeof(adapter->hdd_stats.hdd_arp_stats));
arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
}
}
hdd_debug("STATS_SET_START Received flag %d!", arp_stats_params.flag);
arp_stats_params.vdev_id = adapter->vdev_id;
mac_handle = hdd_ctx->mac_handle;
if (QDF_STATUS_SUCCESS !=
sme_set_nud_debug_stats(mac_handle, &arp_stats_params)) {
hdd_err("STATS_SET_START CMD Failed!");
return -EINVAL;
}
hdd_exit();
return err;
}
/**
* wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to send arp stats to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_nud_stats(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#undef STATS_SET_INVALID
#undef STATS_SET_START
#undef STATS_GW_IPV4
#undef STATS_SET_MAX
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_setarp_stats_cmd()
*/
#define STATS_GET_INVALID \
QCA_ATTR_NUD_STATS_SET_INVALID
#define COUNT_FROM_NETDEV \
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV
#define COUNT_TO_LOWER_MAC \
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC
#define RX_COUNT_BY_LOWER_MAC \
QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC
#define COUNT_TX_SUCCESS \
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS
#define RSP_RX_COUNT_BY_LOWER_MAC \
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC
#define RSP_RX_COUNT_BY_UPPER_MAC \
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC
#define RSP_COUNT_TO_NETDEV \
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV
#define RSP_COUNT_OUT_OF_ORDER_DROP \
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP
#define AP_LINK_ACTIVE \
QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE
#define AP_LINK_DAD \
QCA_ATTR_NUD_STATS_IS_DAD
#define DATA_PKT_STATS \
QCA_ATTR_NUD_STATS_DATA_PKT_STATS
#define STATS_GET_MAX \
QCA_ATTR_NUD_STATS_GET_MAX
#define CHECK_STATS_INVALID \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_INVALID
#define CHECK_STATS_PKT_TYPE \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_TYPE
#define CHECK_STATS_PKT_DNS_DOMAIN_NAME \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DNS_DOMAIN_NAME
#define CHECK_STATS_PKT_SRC_PORT \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_SRC_PORT
#define CHECK_STATS_PKT_DEST_PORT \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_PORT
#define CHECK_STATS_PKT_DEST_IPV4 \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV4
#define CHECK_STATS_PKT_DEST_IPV6 \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV6
#define CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV
#define CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC
#define CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC
#define CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS
#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC
#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC
#define CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV
#define CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP \
QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP
#define CHECK_DATA_STATS_MAX \
QCA_ATTR_CONNECTIVITY_CHECK_DATA_STATS_MAX
const struct nla_policy
qca_wlan_vendor_get_nud_stats[STATS_GET_MAX + 1] = {
[COUNT_FROM_NETDEV] = {.type = NLA_U16 },
[COUNT_TO_LOWER_MAC] = {.type = NLA_U16 },
[RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 },
[COUNT_TX_SUCCESS] = {.type = NLA_U16 },
[RSP_RX_COUNT_BY_LOWER_MAC] = {.type = NLA_U16 },
[RSP_RX_COUNT_BY_UPPER_MAC] = {.type = NLA_U16 },
[RSP_COUNT_TO_NETDEV] = {.type = NLA_U16 },
[RSP_COUNT_OUT_OF_ORDER_DROP] = {.type = NLA_U16 },
[AP_LINK_ACTIVE] = {.type = NLA_FLAG },
[AP_LINK_DAD] = {.type = NLA_FLAG },
[DATA_PKT_STATS] = {.type = NLA_U16 },
};
/**
* hdd_populate_dns_stats_info() - send dns stats info to network stack
* @adapter: pointer to adapter context
* @skb: pointer to skb
*
*
* Return: An error code or 0 on success.
*/
static int hdd_populate_dns_stats_info(struct hdd_adapter *adapter,
struct sk_buff *skb)
{
uint8_t *dns_query;
dns_query = qdf_mem_malloc(adapter->track_dns_domain_len + 1);
if (!dns_query)
return -EINVAL;
qdf_mem_copy(dns_query, adapter->dns_payload,
adapter->track_dns_domain_len);
if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
CONNECTIVITY_CHECK_SET_DNS) ||
nla_put(skb, CHECK_STATS_PKT_DNS_DOMAIN_NAME,
adapter->track_dns_domain_len,
hdd_dns_unmake_name_query(dns_query)) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
adapter->hdd_stats.hdd_dns_stats.tx_dns_req_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
adapter->hdd_stats.hdd_dns_stats.rx_dns_rsp_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
adapter->hdd_stats.hdd_dns_stats.rx_delivered) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
adapter->hdd_stats.hdd_dns_stats.rx_host_drop)) {
hdd_err("nla put fail");
qdf_mem_free(dns_query);
kfree_skb(skb);
return -EINVAL;
}
qdf_mem_free(dns_query);
return 0;
}
/**
* hdd_populate_tcp_stats_info() - send tcp stats info to network stack
* @adapter: pointer to adapter context
* @skb: pointer to skb
* @pkt_type: tcp pkt type
*
* Return: An error code or 0 on success.
*/
static int hdd_populate_tcp_stats_info(struct hdd_adapter *adapter,
struct sk_buff *skb,
uint8_t pkt_type)
{
switch (pkt_type) {
case CONNECTIVITY_CHECK_SET_TCP_SYN:
/* Fill info for tcp syn packets (tx packet) */
if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
CONNECTIVITY_CHECK_SET_TCP_SYN) ||
nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
adapter->track_src_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
adapter->track_dest_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hdd_tcp_stats.
tx_tcp_syn_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_tcp_stats.
tx_tcp_syn_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt)) {
hdd_err("nla put fail");
kfree_skb(skb);
return -EINVAL;
}
break;
case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK:
/* Fill info for tcp syn-ack packets (rx packet) */
if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
CONNECTIVITY_CHECK_SET_TCP_SYN_ACK) ||
nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
adapter->track_src_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
adapter->track_dest_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
adapter->hdd_stats.hdd_tcp_stats.
rx_tcp_syn_ack_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
adapter->hdd_stats.hdd_tcp_stats.rx_delivered) ||
nla_put_u16(skb,
CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
adapter->hdd_stats.hdd_tcp_stats.rx_host_drop)) {
hdd_err("nla put fail");
kfree_skb(skb);
return -EINVAL;
}
break;
case CONNECTIVITY_CHECK_SET_TCP_ACK:
/* Fill info for tcp ack packets (tx packet) */
if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
CONNECTIVITY_CHECK_SET_TCP_ACK) ||
nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
adapter->track_src_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
adapter->track_dest_port) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hdd_tcp_stats.
tx_tcp_ack_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_tcp_stats.
tx_tcp_ack_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt)) {
hdd_err("nla put fail");
kfree_skb(skb);
return -EINVAL;
}
break;
default:
break;
}
return 0;
}
/**
* hdd_populate_icmpv4_stats_info() - send icmpv4 stats info to network stack
* @adapter: pointer to adapter context
* @skb: pointer to skb
*
*
* Return: An error code or 0 on success.
*/
static int hdd_populate_icmpv4_stats_info(struct hdd_adapter *adapter,
struct sk_buff *skb)
{
if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
CONNECTIVITY_CHECK_SET_ICMPV4) ||
nla_put_u32(skb, CHECK_STATS_PKT_DEST_IPV4,
adapter->track_dest_ipv4) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
adapter->hdd_stats.hdd_icmpv4_stats.tx_icmpv4_req_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) ||
nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
adapter->hdd_stats.hdd_icmpv4_stats.rx_icmpv4_rsp_count) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered) ||
nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
adapter->hdd_stats.hdd_icmpv4_stats.rx_host_drop)) {
hdd_err("nla put fail");
kfree_skb(skb);
return -EINVAL;
}
return 0;
}
/**
* hdd_populate_connectivity_check_stats_info() - send connectivity stats info
* to network stack
* @adapter: pointer to adapter context
* @skb: pointer to skb
*
*
* Return: An error code or 0 on success.
*/
static int hdd_populate_connectivity_check_stats_info(
struct hdd_adapter *adapter, struct sk_buff *skb)
{
struct nlattr *connect_stats, *connect_info;
uint32_t count = 0;
connect_stats = nla_nest_start(skb, DATA_PKT_STATS);
if (!connect_stats) {
hdd_err("nla_nest_start failed");
return -EINVAL;
}
if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_DNS) {
connect_info = nla_nest_start(skb, count);
if (!connect_info) {
hdd_err("nla_nest_start failed count %u", count);
return -EINVAL;
}
if (hdd_populate_dns_stats_info(adapter, skb))
goto put_attr_fail;
nla_nest_end(skb, connect_info);
count++;
}
if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) {
connect_info = nla_nest_start(skb, count);
if (!connect_info) {
hdd_err("nla_nest_start failed count %u", count);
return -EINVAL;
}
if (hdd_populate_tcp_stats_info(adapter, skb,
CONNECTIVITY_CHECK_SET_TCP_SYN))
goto put_attr_fail;
nla_nest_end(skb, connect_info);
count++;
connect_info = nla_nest_start(skb, count);
if (!connect_info) {
hdd_err("nla_nest_start failed count %u", count);
return -EINVAL;
}
if (hdd_populate_tcp_stats_info(adapter, skb,
CONNECTIVITY_CHECK_SET_TCP_SYN_ACK))
goto put_attr_fail;
nla_nest_end(skb, connect_info);
count++;
connect_info = nla_nest_start(skb, count);
if (!connect_info) {
hdd_err("nla_nest_start failed count %u", count);
return -EINVAL;
}
if (hdd_populate_tcp_stats_info(adapter, skb,
CONNECTIVITY_CHECK_SET_TCP_ACK))
goto put_attr_fail;
nla_nest_end(skb, connect_info);
count++;
}
if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ICMPV4) {
connect_info = nla_nest_start(skb, count);
if (!connect_info) {
hdd_err("nla_nest_start failed count %u", count);
return -EINVAL;
}
if (hdd_populate_icmpv4_stats_info(adapter, skb))
goto put_attr_fail;
nla_nest_end(skb, connect_info);
count++;
}
nla_nest_end(skb, connect_stats);
return 0;
put_attr_fail:
hdd_err("QCA_WLAN_VENDOR_ATTR put fail. count %u", count);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to get arp stats to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int err = 0;
struct net_device *dev = wdev->netdev;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct get_arp_stats_params arp_stats_params;
mac_handle_t mac_handle;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
uint32_t pkt_type_bitmap;
struct sk_buff *skb;
struct osif_request *request = NULL;
static const struct osif_request_params params = {
.priv_size = 0,
.timeout_ms = WLAN_WAIT_TIME_NUD_STATS,
};
void *cookie = NULL;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
err = wlan_hdd_validate_context(hdd_ctx);
if (0 != err)
return err;
err = hdd_validate_adapter(adapter);
if (err)
return err;
if (adapter->device_mode != QDF_STA_MODE) {
hdd_err("STATS supported in only STA mode!");
return -EINVAL;
}
request = osif_request_alloc(&params);
if (!request) {
hdd_err("Request allocation failure");
return -ENOMEM;
}
cookie = osif_request_cookie(request);
arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
arp_stats_params.vdev_id = adapter->vdev_id;
pkt_type_bitmap = adapter->pkt_type_bitmap;
/* send NUD failure event only when ARP tracking is enabled. */
if (cdp_cfg_get(soc, cfg_dp_enable_data_stall) &&
(pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ARP))
cdp_post_data_stall_event(soc,
DATA_STALL_LOG_INDICATOR_FRAMEWORK,
DATA_STALL_LOG_NUD_FAILURE,
0xFF, 0XFF,
DATA_STALL_LOG_RECOVERY_TRIGGER_PDR);
mac_handle = hdd_ctx->mac_handle;
if (sme_set_nud_debug_stats_cb(mac_handle, hdd_get_nud_stats_cb,
cookie) != QDF_STATUS_SUCCESS) {
hdd_err("Setting NUD debug stats callback failure");
err = -EINVAL;
goto exit;
}
if (QDF_STATUS_SUCCESS !=
sme_get_nud_debug_stats(mac_handle, &arp_stats_params)) {
hdd_err("STATS_SET_START CMD Failed!");
err = -EINVAL;
goto exit;
}
err = osif_request_wait_for_response(request);
if (err) {
hdd_err("SME timedout while retrieving NUD stats");
err = -ETIMEDOUT;
goto exit;
}
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
WLAN_NUD_STATS_LEN);
if (!skb) {
hdd_err("%s: cfg80211_vendor_cmd_alloc_reply_skb failed",
__func__);
err = -ENOMEM;
goto exit;
}
if (nla_put_u16(skb, COUNT_FROM_NETDEV,
adapter->hdd_stats.hdd_arp_stats.tx_arp_req_count) ||
nla_put_u16(skb, COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) ||
nla_put_u16(skb, RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_arp_stats.tx_host_fw_sent) ||
nla_put_u16(skb, COUNT_TX_SUCCESS,
adapter->hdd_stats.hdd_arp_stats.tx_ack_cnt) ||
nla_put_u16(skb, RSP_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hdd_arp_stats.rx_fw_cnt) ||
nla_put_u16(skb, RSP_RX_COUNT_BY_UPPER_MAC,
adapter->hdd_stats.hdd_arp_stats.rx_arp_rsp_count) ||
nla_put_u16(skb, RSP_COUNT_TO_NETDEV,
adapter->hdd_stats.hdd_arp_stats.rx_delivered) ||
nla_put_u16(skb, RSP_COUNT_OUT_OF_ORDER_DROP,
adapter->hdd_stats.hdd_arp_stats.
rx_host_drop_reorder)) {
hdd_err("nla put fail");
kfree_skb(skb);
err = -EINVAL;
goto exit;
}
if (adapter->con_status)
nla_put_flag(skb, AP_LINK_ACTIVE);
if (adapter->dad)
nla_put_flag(skb, AP_LINK_DAD);
/* ARP tracking is done above. */
pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP;
if (pkt_type_bitmap) {
if (hdd_populate_connectivity_check_stats_info(adapter, skb)) {
err = -EINVAL;
goto exit;
}
}
cfg80211_vendor_cmd_reply(skb);
exit:
osif_request_put(request);
return err;
}
/**
* wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to get arp stats to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_nud_stats(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#undef QCA_ATTR_NUD_STATS_SET_INVALID
#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV
#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC
#undef QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC
#undef QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS
#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC
#undef QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC
#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV
#undef QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP
#undef QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE
#undef QCA_ATTR_NUD_STATS_GET_MAX
void hdd_bt_activity_cb(hdd_handle_t hdd_handle, uint32_t bt_activity)
{
struct hdd_context *hdd_ctx = hdd_handle_to_context(hdd_handle);
int status;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_ADD)
hdd_ctx->bt_a2dp_active = 1;
else if (bt_activity == WLAN_COEX_EVENT_BT_A2DP_PROFILE_REMOVE)
hdd_ctx->bt_a2dp_active = 0;
else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_ADD)
hdd_ctx->bt_vo_active = 1;
else if (bt_activity == WLAN_COEX_EVENT_BT_VOICE_PROFILE_REMOVE)
hdd_ctx->bt_vo_active = 0;
else
return;
ucfg_scan_set_bt_activity(hdd_ctx->psoc, hdd_ctx->bt_a2dp_active);
hdd_debug("a2dp_active: %d vo_active: %d", hdd_ctx->bt_a2dp_active,
hdd_ctx->bt_vo_active);
}
struct chain_rssi_priv {
struct chain_rssi_result chain_rssi;
};
/**
* hdd_get_chain_rssi_cb() - Callback function to get chain rssi
* @context: opaque context originally passed to SME. HDD always passes
* a cookie for the request context
* @data: struct for get chain rssi
*
* This function receives the response/data from the lower layer and
* checks to see if the thread is still waiting then post the results to
* upper layer, if the request has timed out then ignore.
*
* Return: None
*/
static void hdd_get_chain_rssi_cb(void *context,
struct chain_rssi_result *data)
{
struct osif_request *request;
struct chain_rssi_priv *priv;
hdd_enter();
request = osif_request_get(context);
if (!request) {
hdd_err("Obsolete request");
return;
}
priv = osif_request_priv(request);
priv->chain_rssi = *data;
osif_request_complete(request);
osif_request_put(request);
}
/**
* hdd_post_get_chain_rssi_rsp - send rsp to user space
* @hdd_ctx: pointer to hdd context
* @result: chain rssi result
*
* Return: 0 for success, non-zero for failure
*/
static int hdd_post_get_chain_rssi_rsp(struct hdd_context *hdd_ctx,
struct chain_rssi_result *result)
{
struct sk_buff *skb;
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
(sizeof(result->chain_rssi) + NLA_HDRLEN) +
(sizeof(result->chain_evm) + NLA_HDRLEN) +
(sizeof(result->ant_id) + NLA_HDRLEN) +
NLMSG_HDRLEN);
if (!skb) {
hdd_err("cfg80211_vendor_event_alloc failed");
return -ENOMEM;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI,
sizeof(result->chain_rssi),
result->chain_rssi)) {
hdd_err("put fail");
goto nla_put_failure;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_CHAIN_EVM,
sizeof(result->chain_evm),
result->chain_evm)) {
hdd_err("put fail");
goto nla_put_failure;
}
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO,
sizeof(result->ant_id),
result->ant_id)) {
hdd_err("put fail");
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* Return: 0 on success; error number otherwise.
*/
static int __wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
mac_handle_t mac_handle;
struct get_chain_rssi_req_params req_msg;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
QDF_STATUS status;
int retval;
void *cookie;
struct osif_request *request;
struct chain_rssi_priv *priv;
static const struct osif_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
hdd_enter();
retval = wlan_hdd_validate_context(hdd_ctx);
if (0 != retval)
return retval;
if (wlan_cfg80211_nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX,
data, data_len, NULL)) {
hdd_err("Invalid ATTR");
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hdd_err("attr mac addr failed");
return -EINVAL;
}
if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) !=
QDF_MAC_ADDR_SIZE) {
hdd_err("incorrect mac size");
return -EINVAL;
}
memcpy(&req_msg.peer_macaddr,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
QDF_MAC_ADDR_SIZE);
req_msg.session_id = adapter->vdev_id;
request = osif_request_alloc(&params);
if (!request) {
hdd_err("Request allocation failure");
return -ENOMEM;
}
cookie = osif_request_cookie(request);
mac_handle = hdd_ctx->mac_handle;
status = sme_get_chain_rssi(mac_handle,
&req_msg,
hdd_get_chain_rssi_cb,
cookie);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Unable to get chain rssi");
retval = qdf_status_to_os_return(status);
} else {
retval = osif_request_wait_for_response(request);
if (retval) {
hdd_err("Target response timed out");
} else {
priv = osif_request_priv(request);
retval = hdd_post_get_chain_rssi_rsp(hdd_ctx,
&priv->chain_rssi);
if (retval)
hdd_err("Failed to post chain rssi");
}
}
osif_request_put(request);
hdd_exit();
return retval;
}
/**
* wlan_hdd_cfg80211_get_chain_rssi() - get chain rssi
* @wiphy: wiphy pointer
* @wdev: pointer to struct wireless_dev
* @data: pointer to incoming NL vendor data
* @data_len: length of @data
*
* Return: 0 on success; error number otherwise.
*/
static int wlan_hdd_cfg80211_get_chain_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_chain_rssi(wiphy, wdev, data, data_len);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_fill_intf_info() - Fill skb buffer with interface info
* @skb: Pointer to skb
* @info: mac mode info
* @index: attribute type index for nla_nest_start()
*
* Return : 0 on success and errno on failure
*/
static int wlan_hdd_fill_intf_info(struct sk_buff *skb,
struct connection_info *info, int index)
{
struct nlattr *attr;
uint32_t freq;
struct hdd_context *hdd_ctx;
struct hdd_adapter *hdd_adapter;
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
if (!hdd_ctx)
goto error;
hdd_adapter = hdd_get_adapter_by_vdev(hdd_ctx, info->vdev_id);
if (!hdd_adapter)
goto error;
attr = nla_nest_start(skb, index);
if (!attr)
goto error;
freq = sme_chn_to_freq(info->channel);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX,
hdd_adapter->dev->ifindex) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ, freq))
goto error;
nla_nest_end(skb, attr);
return 0;
error:
hdd_err("Fill buffer with interface info failed");
return -EINVAL;
}
/**
* wlan_hdd_fill_mac_info() - Fill skb buffer with mac info
* @skb: Pointer to skb
* @info: mac mode info
* @mac_id: MAC id
* @conn_count: number of current connections
*
* Return : 0 on success and errno on failure
*/
static int wlan_hdd_fill_mac_info(struct sk_buff *skb,
struct connection_info *info, uint32_t mac_id,
uint32_t conn_count)
{
struct nlattr *attr, *intf_attr;
uint32_t band = 0, i = 0, j = 0;
bool present = false;
while (i < conn_count) {
if (info[i].mac_id == mac_id) {
present = true;
if (info[i].channel <= SIR_11B_CHANNEL_END)
band |= 1 << NL80211_BAND_2GHZ;
else if (info[i].channel <= SIR_11A_CHANNEL_END)
band |= 1 << NL80211_BAND_5GHZ;
}
i++;
}
if (!present)
return 0;
i = 0;
attr = nla_nest_start(skb, mac_id);
if (!attr)
goto error;
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID, mac_id) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND, band))
goto error;
intf_attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO);
if (!intf_attr)
goto error;
while (i < conn_count) {
if (info[i].mac_id == mac_id) {
if (wlan_hdd_fill_intf_info(skb, &info[i], j))
return -EINVAL;
j++;
}
i++;
}
nla_nest_end(skb, intf_attr);
nla_nest_end(skb, attr);
return 0;
error:
hdd_err("Fill buffer with mac info failed");
return -EINVAL;
}
int wlan_hdd_send_mode_change_event(void)
{
int err;
struct hdd_context *hdd_ctx;
struct sk_buff *skb;
struct nlattr *attr;
struct connection_info info[MAX_NUMBER_OF_CONC_CONNECTIONS];
uint32_t conn_count, mac_id;
hdd_enter();
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
if (!hdd_ctx) {
hdd_err("HDD context is NULL");
return -EINVAL;
}
err = wlan_hdd_validate_context(hdd_ctx);
if (0 != err)
return err;
conn_count = policy_mgr_get_connection_info(hdd_ctx->psoc, info);
if (!conn_count)
return -EINVAL;
skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
(sizeof(uint32_t) * 4) *
MAX_NUMBER_OF_CONC_CONNECTIONS + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO_INDEX,
GFP_KERNEL);
if (!skb) {
hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
attr = nla_nest_start(skb, QCA_WLAN_VENDOR_ATTR_MAC_INFO);
if (!attr) {
hdd_err("nla_nest_start failed");
kfree_skb(skb);
return -EINVAL;
}
for (mac_id = 0; mac_id < MAX_MAC; mac_id++) {
if (wlan_hdd_fill_mac_info(skb, info, mac_id, conn_count)) {
kfree_skb(skb);
return -EINVAL;
}
}
nla_nest_end(skb, attr);
cfg80211_vendor_event(skb, GFP_KERNEL);
hdd_exit();
return err;
}
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_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 | WIPHY_VENDOR_CMD_NEED_RUNNING,
.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 | WIPHY_VENDOR_CMD_NEED_RUNNING,
.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
{
.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 |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_scanning_mac_oui
},
FEATURE_CONCURRENCY_MATRIX_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_disable_dfs_chan_scan
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WISA,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_handle_wisa_cmd
},
FEATURE_STATION_INFO_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DO_ACS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_do_acs
},
{
.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
#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
},
#ifndef WLAN_UMAC_CONVERGENCE
{
.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
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_wifi_test_config
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ROAM,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_ext_roam_params
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_wifi_logger_start
},
{
.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 |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_wifi_logger_get_ring_data
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_preferred_freq_list
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_probable_oper_channel
},
#ifdef WLAN_FEATURE_TSF
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TSF,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_handle_tsf_cmd
},
#endif
#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
#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
FEATURE_RSSI_MONITOR_VENDOR_COMMANDS
#ifdef WLAN_NS_OFFLOAD
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_ns_offload
},
#endif /* WLAN_NS_OFFLOAD */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_logger_supp_feature
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_vendor_scan
},
/* Vendor abort scan */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_vendor_abort_scan
},
/* 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_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
},
FEATURE_OTA_TEST_VENDOR_COMMANDS
#ifdef FEATURE_LFR_SUBNET_DETECTION
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_gateway_params
},
#endif /* FEATURE_LFR_SUBNET_DETECTION */
FEATURE_TX_POWER_VENDOR_COMMANDS
#ifdef FEATURE_WLAN_APF
{
.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_apf_offload
},
#endif /* FEATURE_WLAN_APF */
{
.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
},
FEATURE_P2P_LISTEN_OFFLOAD_VENDOR_COMMANDS
FEATURE_SAP_COND_CHAN_SWITCH_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_wakelock_stats
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_bus_size
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_update_vendor_channel
},
{
.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_ROAMING,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_fast_roaming
},
#ifdef WLAN_FEATURE_DISA
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_encrypt_decrypt_msg
},
#endif
#ifdef FEATURE_WLAN_TDLS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd =
QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_configure_tdls_mode
},
#endif
FEATURE_SAR_LIMITS_VENDOR_COMMANDS
BCN_RECV_FEATURE_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_sar_power_limits
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_trace_level
},
{
.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_NUD_STATS_SET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_set_nud_stats
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_nud_stats
},
FEATURE_BSS_TRANSITION_VENDOR_COMMANDS
FEATURE_SPECTRAL_SCAN_VENDOR_COMMANDS
#ifdef WLAN_UMAC_CONVERGENCE
COMMON_VENDOR_COMMANDS
#endif
FEATURE_11AX_VENDOR_COMMANDS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_chain_rssi
},
FEATURE_ACTIVE_TOS_VENDOR_COMMANDS
FEATURE_NAN_VENDOR_COMMANDS
FEATURE_FW_STATE_COMMANDS
FEATURE_COEX_CONFIG_COMMANDS
FEATURE_MPTA_HELPER_COMMANDS
};
struct hdd_context *hdd_cfg80211_wiphy_alloc(void)
{
struct wiphy *wiphy;
struct hdd_context *hdd_ctx;
hdd_enter();
wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, sizeof(*hdd_ctx));
if (!wiphy) {
hdd_err("failed to allocate wiphy!");
return NULL;
}
hdd_ctx = wiphy_priv(wiphy);
hdd_ctx->wiphy = wiphy;
return hdd_ctx;
}
int wlan_hdd_cfg80211_update_band(struct hdd_context *hdd_ctx,
struct wiphy *wiphy,
enum band_info new_band)
{
int i, j;
enum channel_state channel_state;
hdd_enter();
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) {
if (!wiphy->bands[i])
continue;
for (j = 0; j < wiphy->bands[i]->n_channels; j++) {
struct ieee80211_supported_band *band = wiphy->bands[i];
channel_state = wlan_reg_get_channel_state(
hdd_ctx->pdev,
band->channels[j].hw_value);
if (HDD_NL80211_BAND_2GHZ == i &&
BAND_5G == new_band) {
/* 5G only */
#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY
/* Enable Social channels for P2P */
if (WLAN_HDD_IS_SOCIAL_CHANNEL
(band->channels[j].center_freq)
&& CHANNEL_STATE_ENABLE ==
channel_state)
band->channels[j].flags &=
~IEEE80211_CHAN_DISABLED;
else
#endif
band->channels[j].flags |=
IEEE80211_CHAN_DISABLED;
continue;
} else if (HDD_NL80211_BAND_5GHZ == i &&
BAND_2G == new_band) {
/* 2G only */
band->channels[j].flags |=
IEEE80211_CHAN_DISABLED;
continue;
}
if (CHANNEL_STATE_DISABLE != channel_state)
band->channels[j].flags &=
~IEEE80211_CHAN_DISABLED;
}
}
return 0;
}
#if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
{
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
}
#else
static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
{
}
#endif
#define WLAN_HDD_MAX_NUM_CSA_COUNTERS 2
#if defined(CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0))
/**
* wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA
* frames
* @wiphy: Pointer to wiphy
*
* This function is used to indicate the support of source mac address
* randomization of management action frames
*
* Return: None
*/
static void
wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
}
#else
static void
wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
{
}
#endif
#if defined(WLAN_FEATURE_FILS_SK) && \
(defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)))
static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_SK_OFFLOAD);
}
#else
static void wlan_hdd_cfg80211_set_wiphy_fils_feature(struct wiphy *wiphy)
{
}
#endif
#if defined (CFG80211_SCAN_DBS_CONTROL_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0))
static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_SPAN_SCAN);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_LOW_POWER_SCAN);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN);
}
#else
static void wlan_hdd_cfg80211_set_wiphy_scan_flags(struct wiphy *wiphy)
{
}
#endif
#if defined(CFG80211_SCAN_OCE_CAPABILITY_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0))
static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE);
wiphy_ext_feature_set(
wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);
}
#else
static void wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(struct wiphy *wiphy)
{
}
#endif
#if defined(WLAN_FEATURE_SAE) && \
defined(CFG80211_EXTERNAL_AUTH_SUPPORT)
/**
* wlan_hdd_cfg80211_set_wiphy_sae_feature() - Indicates support of SAE feature
* @wiphy: Pointer to wiphy
*
* This function is used to indicate the support of SAE
*
* Return: None
*/
static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
if (ucfg_fwol_get_sae_enable(hdd_ctx->psoc))
wiphy->features |= NL80211_FEATURE_SAE;
}
#else
static void wlan_hdd_cfg80211_set_wiphy_sae_feature(struct wiphy *wiphy)
{
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) || \
defined(CFG80211_DFS_OFFLOAD_BACKPORT)
static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy)
{
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD);
}
#else
static void wlan_hdd_cfg80211_set_dfs_offload_feature(struct wiphy *wiphy)
{
wiphy->flags |= WIPHY_FLAG_DFS_OFFLOAD;
}
#endif
#ifdef WLAN_FEATURE_DSRC
static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg,
int *num_ch, int *ch_len)
{
*num_ch = QDF_ARRAY_SIZE(hdd_channels_dot11p);
*ch_len = sizeof(hdd_channels_dot11p);
}
static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len)
{
if (!ch_arr_len)
return;
qdf_mem_copy(ch_ptr, &hdd_channels_dot11p[0], ch_arr_len);
}
static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg,
int *num_ch, int *ch_len)
{
*num_ch = 0;
*ch_len = 0;
}
static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len)
{
}
/**
* wlan_hdd_populate_srd_chan_info() - Populate SRD chan info in hdd context
* @hdd_ctx: pointer to hdd context
* @index: SRD channel beginning index in chan_info of @hdd_ctx
*
* Return: Number of SRD channels populated
*/
static uint32_t
wlan_hdd_populate_srd_chan_info(struct hdd_context *hdd_ctx, uint32_t index)
{
return 0;
}
#else
static void wlan_hdd_get_num_dsrc_ch_and_len(struct hdd_config *hdd_cfg,
int *num_ch, int *ch_len)
{
*num_ch = 0;
*ch_len = 0;
}
static void wlan_hdd_copy_dsrc_ch(char *ch_ptr, int ch_arr_len)
{
}
static void wlan_hdd_get_num_srd_ch_and_len(struct hdd_config *hdd_cfg,
int *num_ch, int *ch_len)
{
*num_ch = QDF_ARRAY_SIZE(hdd_etsi13_srd_ch);
*ch_len = sizeof(hdd_etsi13_srd_ch);
}
static void wlan_hdd_copy_srd_ch(char *ch_ptr, int ch_arr_len)
{
if (!ch_arr_len)
return;
qdf_mem_copy(ch_ptr, &hdd_etsi13_srd_ch[0], ch_arr_len);
}
/**
* wlan_hdd_populate_srd_chan_info() - Populate SRD chan info in hdd context
* @hdd_ctx: pointer to hdd context
* @index: SRD channel beginning index in chan_info of @hdd_ctx
*
* Return: Number of SRD channels populated
*/
static uint32_t
wlan_hdd_populate_srd_chan_info(struct hdd_context *hdd_ctx, uint32_t index)
{
uint32_t num_srd_ch, i;
struct scan_chan_info *chan_info;
num_srd_ch = QDF_ARRAY_SIZE(hdd_etsi13_srd_ch);
chan_info = hdd_ctx->chan_info;
for (i = 0; i < num_srd_ch; i++)
chan_info[index + i].freq = hdd_etsi13_srd_ch[i].center_freq;
return num_srd_ch;
}
#endif
/*
* FUNCTION: wlan_hdd_cfg80211_init
* This function is called by hdd_wlan_startup()
* during initialization.
* This function is used to initialize and register wiphy structure.
*/
int wlan_hdd_cfg80211_init(struct device *dev,
struct wiphy *wiphy, struct hdd_config *config)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
uint32_t *cipher_suites;
uint8_t allow_mcc_go_diff_bi = 0, enable_mcc = 0;
bool mac_spoofing_enabled;
bool is_oce_sta_enabled;
QDF_STATUS status;
hdd_enter();
/* Now bind the underlying wlan device with wiphy */
set_wiphy_dev(wiphy, dev);
wiphy->mgmt_stypes = wlan_hdd_txrx_stypes;
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, 11, 0))
wiphy->wowlan = &wowlan_support_cfg80211_init;
#else
wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
wiphy->wowlan.n_patterns = WOWL_MAX_PTRNS_ALLOWED;
wiphy->wowlan.pattern_min_len = 1;
wiphy->wowlan.pattern_max_len = WOWL_PTRN_MAX_SIZE;
#endif
#ifdef FEATURE_WLAN_TDLS
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS
| WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
wiphy->features |= NL80211_FEATURE_HT_IBSS;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
#endif
wlan_hdd_cfg80211_set_wiphy_scan_flags(wiphy);
is_oce_sta_enabled = false;
status = ucfg_mlme_get_oce_sta_enabled_info(hdd_ctx->psoc,
&is_oce_sta_enabled);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not get OCE STA enable info");
if (is_oce_sta_enabled)
wlan_hdd_cfg80211_set_wiphy_oce_scan_flags(wiphy);
wlan_hdd_cfg80211_set_wiphy_sae_feature(wiphy);
wlan_scan_cfg80211_add_connected_pno_support(wiphy);
wiphy->max_scan_ssids = MAX_SCAN_SSID;
wiphy->max_scan_ie_len = SIR_MAC_MAX_ADD_IE_LENGTH;
wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_ADHOC)
| BIT(NL80211_IFTYPE_P2P_CLIENT)
| BIT(NL80211_IFTYPE_P2P_GO)
| BIT(NL80211_IFTYPE_AP)
| BIT(NL80211_IFTYPE_MONITOR);
if (QDF_STATUS_SUCCESS !=
ucfg_policy_mgr_get_allow_mcc_go_diff_bi(hdd_ctx->psoc,
&allow_mcc_go_diff_bi))
hdd_err("can't get mcc_go_diff_bi value, use default");
if (QDF_STATUS_SUCCESS !=
ucfg_mlme_get_mcc_feature(hdd_ctx->psoc, &enable_mcc))
hdd_err("can't get enable_mcc value, use default");
if (config->advertise_concurrent_operation) {
if (enable_mcc) {
int i;
for (i = 0;
i < ARRAY_SIZE(wlan_hdd_iface_combination);
i++) {
if (!allow_mcc_go_diff_bi)
wlan_hdd_iface_combination[i].
beacon_int_infra_match = true;
}
}
wiphy->n_iface_combinations =
ARRAY_SIZE(wlan_hdd_iface_combination);
wiphy->iface_combinations = wlan_hdd_iface_combination;
}
/*
* In case of static linked driver at the time of driver unload,
* module exit doesn't happens. Module cleanup helps in cleaning
* of static memory.
* If driver load happens statically, at the time of driver unload,
* wiphy flags don't get reset because of static memory.
* It's better not to store channel in static memory.
* The memory is for channels of struct wiphy and shouldn't be
* released during stop modules. So if it's allocated in active
* domain, the memory leak detector would catch the leak during
* stop modules. To avoid this,alloc in init domain in advance.
*/
hdd_ctx->channels_2ghz = qdf_mem_malloc(band_2_ghz_channels_size);
if (!hdd_ctx->channels_2ghz) {
hdd_err("Not enough memory to allocate channels");
return -ENOMEM;
}
hdd_ctx->channels_5ghz = qdf_mem_malloc(band_5_ghz_chanenls_size);
if (!hdd_ctx->channels_5ghz)
goto mem_fail;
/*Initialise the supported cipher suite details */
if (ucfg_fwol_get_gcmp_enable(hdd_ctx->psoc)) {
cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites) +
sizeof(hdd_gcmp_cipher_suits));
if (!cipher_suites)
return -ENOMEM;
wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites) +
QDF_ARRAY_SIZE(hdd_gcmp_cipher_suits);
qdf_mem_copy(cipher_suites, &hdd_cipher_suites,
sizeof(hdd_cipher_suites));
qdf_mem_copy(cipher_suites + QDF_ARRAY_SIZE(hdd_cipher_suites),
&hdd_gcmp_cipher_suits,
sizeof(hdd_gcmp_cipher_suits));
} else {
cipher_suites = qdf_mem_malloc(sizeof(hdd_cipher_suites));
if (!cipher_suites)
return -ENOMEM;
wiphy->n_cipher_suites = QDF_ARRAY_SIZE(hdd_cipher_suites);
qdf_mem_copy(cipher_suites, &hdd_cipher_suites,
sizeof(hdd_cipher_suites));
}
wiphy->cipher_suites = cipher_suites;
cipher_suites = NULL;
/*signal strength in mBm (100*dBm) */
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION;
if (cds_get_conparam() != QDF_GLOBAL_FTM_MODE) {
wiphy->n_vendor_commands =
ARRAY_SIZE(hdd_wiphy_vendor_commands);
wiphy->vendor_commands = hdd_wiphy_vendor_commands;
wiphy->vendor_events = wlan_hdd_cfg80211_vendor_events;
wiphy->n_vendor_events =
ARRAY_SIZE(wlan_hdd_cfg80211_vendor_events);
}
#ifdef QCA_HT_2040_COEX
wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
#endif
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) || \
defined(CFG80211_BEACON_TX_RATE_CUSTOM_BACKPORT)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT);
#endif
hdd_add_channel_switch_support(&wiphy->flags);
wiphy->max_num_csa_counters = WLAN_HDD_MAX_NUM_CSA_COUNTERS;
mac_spoofing_enabled = ucfg_scan_is_mac_spoofing_enabled(hdd_ctx->psoc);
if (mac_spoofing_enabled)
wlan_hdd_cfg80211_scan_randomization_init(wiphy);
wlan_hdd_cfg80211_action_frame_randomization_init(wiphy);
hdd_exit();
return 0;
mem_fail:
hdd_err("Not enough memory to allocate channels");
if (hdd_ctx->channels_2ghz) {
qdf_mem_free(hdd_ctx->channels_2ghz);
hdd_ctx->channels_2ghz = NULL;
}
return -ENOMEM;
}
/**
* wlan_hdd_cfg80211_deinit() - Deinit cfg80211
* @wiphy: the wiphy to validate against
*
* this function deinit cfg80211 and cleanup the
* memory allocated in wlan_hdd_cfg80211_init also
* reset the global reg params.
*
* Return: void
*/
void wlan_hdd_cfg80211_deinit(struct wiphy *wiphy)
{
int i;
const uint32_t *cipher_suites;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) {
if (wiphy->bands[i] &&
(wiphy->bands[i]->channels))
wiphy->bands[i]->channels = NULL;
}
qdf_mem_free(hdd_ctx->channels_2ghz);
qdf_mem_free(hdd_ctx->channels_5ghz);
hdd_ctx->channels_2ghz = NULL;
hdd_ctx->channels_5ghz = NULL;
cipher_suites = wiphy->cipher_suites;
wiphy->cipher_suites = NULL;
wiphy->n_cipher_suites = 0;
qdf_mem_free((uint32_t *)cipher_suites);
cipher_suites = NULL;
hdd_reset_global_reg_params();
}
/**
* wlan_hdd_update_band_cap() - update capabilities for supported bands
* @hdd_ctx: HDD context
*
* this function will update capabilities for supported bands
*
* Return: void
*/
static void wlan_hdd_update_ht_cap(struct hdd_context *hdd_ctx)
{
struct mlme_ht_capabilities_info ht_cap_info = {0};
QDF_STATUS status;
uint32_t channel_bonding_mode;
status = ucfg_mlme_get_ht_cap_info(hdd_ctx->psoc, &ht_cap_info);
if (QDF_STATUS_SUCCESS != status)
hdd_err("could not get HT capability info");
if (ht_cap_info.tx_stbc) {
if (hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ])
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]->ht_cap.cap |=
IEEE80211_HT_CAP_TX_STBC;
if (hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ])
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]->ht_cap.cap |=
IEEE80211_HT_CAP_TX_STBC;
}
if (!sme_is_feature_supported_by_fw(DOT11AC)) {
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]->
vht_cap.vht_supported = 0;
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_2GHZ]->vht_cap.cap = 0;
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]->
vht_cap.vht_supported = 0;
hdd_ctx->wiphy->bands[HDD_NL80211_BAND_5GHZ]->vht_cap.cap = 0;
}
if (!ht_cap_info.short_gi_20_mhz) {
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 (!ht_cap_info.short_gi_40_mhz)
wlan_hdd_band_5_ghz.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc, &channel_bonding_mode);
if (!channel_bonding_mode)
wlan_hdd_band_5_ghz.ht_cap.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
}
/**
* wlan_hdd_update_band_cap_in_wiphy() - update channel flags based on band cap
* @hdd_ctx: HDD context
*
* This function updates the channel flags based on the band capability set
* in the MLME CFG
*
* Return: void
*/
static void wlan_hdd_update_band_cap_in_wiphy(struct hdd_context *hdd_ctx)
{
int i, j;
uint8_t band_capability;
QDF_STATUS status;
struct ieee80211_supported_band *band;
status = ucfg_mlme_get_band_capability(hdd_ctx->psoc, &band_capability);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to get MLME Band Capability");
return;
}
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++) {
if (!hdd_ctx->wiphy->bands[i])
continue;
for (j = 0; j < hdd_ctx->wiphy->bands[i]->n_channels; j++) {
band = hdd_ctx->wiphy->bands[i];
if (HDD_NL80211_BAND_2GHZ == i &&
BAND_5G == band_capability) {
/* 5G only */
#ifdef WLAN_ENABLE_SOCIAL_CHANNELS_5G_ONLY
/* Enable social channels for P2P */
if (WLAN_HDD_IS_SOCIAL_CHANNEL
(band->channels[j].center_freq))
band->channels[j].flags &=
~IEEE80211_CHAN_DISABLED;
else
#endif
band->channels[j].flags |=
IEEE80211_CHAN_DISABLED;
continue;
} else if (HDD_NL80211_BAND_5GHZ == i &&
BAND_2G == band_capability) {
/* 2G only */
band->channels[j].flags |=
IEEE80211_CHAN_DISABLED;
continue;
}
}
}
}
#ifdef FEATURE_WLAN_ESE
/**
* wlan_hdd_update_lfr_wiphy() - update LFR flag based on configures
* @hdd_ctx: HDD context
*
* This function updates the LFR flag based on LFR configures
*
* Return: void
*/
static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx)
{
bool fast_transition_enabled;
bool lfr_enabled;
bool ese_enabled;
ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc,
&fast_transition_enabled);
ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled);
ucfg_mlme_is_ese_enabled(hdd_ctx->psoc, &ese_enabled);
if (fast_transition_enabled || lfr_enabled || ese_enabled)
hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
}
#else
static void wlan_hdd_update_lfr_wiphy(struct hdd_context *hdd_ctx)
{
bool fast_transition_enabled;
bool lfr_enabled;
ucfg_mlme_is_fast_transition_enabled(hdd_ctx->psoc,
&fast_transition_enabled);
ucfg_mlme_is_lfr_enabled(hdd_ctx->psoc, &lfr_enabled);
if (fast_transition_enabled || lfr_enabled)
hdd_ctx->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
}
#endif
/*
* In this function, wiphy structure is updated after QDF
* initialization. In wlan_hdd_cfg80211_init, only the
* default values will be initialized. The final initialization
* of all required members can be done here.
*/
void wlan_hdd_update_wiphy(struct hdd_context *hdd_ctx)
{
int value;
bool fils_enabled;
bool dfs_master_capable = true;
QDF_STATUS status;
ucfg_mlme_get_sap_max_peers(hdd_ctx->psoc, &value);
hdd_ctx->wiphy->max_ap_assoc_sta = value;
wlan_hdd_update_ht_cap(hdd_ctx);
wlan_hdd_update_band_cap_in_wiphy(hdd_ctx);
wlan_hdd_update_lfr_wiphy(hdd_ctx);
fils_enabled = 0;
status = ucfg_mlme_get_fils_enabled_info(hdd_ctx->psoc,
&fils_enabled);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("could not get fils enabled info");
if (fils_enabled)
wlan_hdd_cfg80211_set_wiphy_fils_feature(hdd_ctx->wiphy);
status = ucfg_mlme_get_dfs_master_capability(hdd_ctx->psoc,
&dfs_master_capable);
if (QDF_IS_STATUS_SUCCESS(status) && dfs_master_capable)
wlan_hdd_cfg80211_set_dfs_offload_feature(hdd_ctx->wiphy);
}
/**
* wlan_hdd_update_11n_mode - update 11n mode in hdd cfg
* @cfg: hdd cfg
*
* this function update 11n mode in hdd cfg
*
* Return: void
*/
void wlan_hdd_update_11n_mode(struct hdd_context *hdd_ctx)
{
struct hdd_config *cfg = hdd_ctx->config;
if (sme_is_feature_supported_by_fw(DOT11AC)) {
hdd_debug("support 11ac");
} else {
hdd_debug("not support 11ac");
if ((cfg->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) ||
(cfg->dot11Mode == eHDD_DOT11_MODE_11ac)) {
cfg->dot11Mode = eHDD_DOT11_MODE_11n;
ucfg_mlme_set_sap_11ac_override(hdd_ctx->psoc, 0);
ucfg_mlme_set_go_11ac_override(hdd_ctx->psoc, 0);
}
}
}
QDF_STATUS wlan_hdd_update_wiphy_supported_band(struct hdd_context *hdd_ctx)
{
int len_5g_ch, num_ch;
int num_dsrc_ch, len_dsrc_ch, num_srd_ch, len_srd_ch;
struct hdd_config *cfg = hdd_ctx->config;
struct wiphy *wiphy = hdd_ctx->wiphy;
if (!hdd_ctx->channels_2ghz)
return QDF_STATUS_E_NOMEM;
wiphy->bands[HDD_NL80211_BAND_2GHZ] = &wlan_hdd_band_2_4_ghz;
wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels = hdd_ctx->channels_2ghz;
qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels,
&hdd_channels_2_4_ghz[0], sizeof(hdd_channels_2_4_ghz));
if (!hdd_is_5g_supported(hdd_ctx) ||
(eHDD_DOT11_MODE_11b == cfg->dot11Mode) ||
(eHDD_DOT11_MODE_11g == cfg->dot11Mode) ||
(eHDD_DOT11_MODE_11b_ONLY == cfg->dot11Mode) ||
(eHDD_DOT11_MODE_11g_ONLY == cfg->dot11Mode))
return QDF_STATUS_SUCCESS;
if (!hdd_ctx->channels_5ghz)
return QDF_STATUS_E_NOMEM;
wiphy->bands[HDD_NL80211_BAND_5GHZ] = &wlan_hdd_band_5_ghz;
wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels = hdd_ctx->channels_5ghz;
wlan_hdd_get_num_dsrc_ch_and_len(cfg, &num_dsrc_ch, &len_dsrc_ch);
wlan_hdd_get_num_srd_ch_and_len(cfg, &num_srd_ch, &len_srd_ch);
num_ch = QDF_ARRAY_SIZE(hdd_channels_5_ghz) + num_dsrc_ch + num_srd_ch;
len_5g_ch = sizeof(hdd_channels_5_ghz);
wiphy->bands[HDD_NL80211_BAND_5GHZ]->n_channels = num_ch;
qdf_mem_copy(wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels,
&hdd_channels_5_ghz[0], len_5g_ch);
if (num_dsrc_ch)
wlan_hdd_copy_dsrc_ch((char *)wiphy->bands[
HDD_NL80211_BAND_5GHZ]->channels +
len_5g_ch, len_dsrc_ch);
if (num_srd_ch)
wlan_hdd_copy_srd_ch((char *)wiphy->bands[
HDD_NL80211_BAND_5GHZ]->channels +
len_5g_ch, len_srd_ch);
return QDF_STATUS_SUCCESS;
}
/* In this function we are registering wiphy. */
int wlan_hdd_cfg80211_register(struct wiphy *wiphy)
{
hdd_enter();
/* Register our wiphy dev with cfg80211 */
if (0 > wiphy_register(wiphy)) {
hdd_err("wiphy register failed");
return -EIO;
}
hdd_exit();
return 0;
}
/* This function registers for all frame which supplicant is interested in */
int wlan_hdd_cfg80211_register_frames(struct hdd_adapter *adapter)
{
mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter);
/* Register for all P2P action, public action etc frames */
uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4);
QDF_STATUS status;
hdd_enter();
if (adapter->device_mode == QDF_FTM_MODE) {
hdd_info("No need to register frames in FTM mode");
return 0;
}
/* Register frame indication call back */
status = sme_register_mgmt_frame_ind_callback(mac_handle,
hdd_indicate_mgmt_frame);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register hdd_indicate_mgmt_frame");
goto ret_status;
}
/* Right now we are registering these frame when driver is getting
* initialized. Once we will move to 2.6.37 kernel, in which we have
* frame register ops, we will move this code as a part of that
*/
/* GAS Initial Request */
status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_REQ,
GAS_INITIAL_REQ_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register GAS_INITIAL_REQ");
goto ret_status;
}
/* GAS Initial Response */
status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_RSP,
GAS_INITIAL_RSP_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register GAS_INITIAL_RSP");
goto dereg_gas_initial_req;
}
/* GAS Comeback Request */
status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_REQ,
GAS_COMEBACK_REQ_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register GAS_COMEBACK_REQ");
goto dereg_gas_initial_rsp;
}
/* GAS Comeback Response */
status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_RSP,
GAS_COMEBACK_RSP_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register GAS_COMEBACK_RSP");
goto dereg_gas_comeback_req;
}
/* WNM BSS Transition Request frame */
status = sme_register_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) WNM_BSS_ACTION_FRAME,
WNM_BSS_ACTION_FRAME_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register WNM_BSS_ACTION_FRAME");
goto dereg_gas_comeback_rsp;
}
/* WNM-Notification */
status = sme_register_mgmt_frame(mac_handle, adapter->vdev_id, type,
(uint8_t *) WNM_NOTIFICATION_FRAME,
WNM_NOTIFICATION_FRAME_SIZE);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to register WNM_NOTIFICATION_FRAME");
goto dereg_wnm_bss_action_frm;
}
return 0;
dereg_wnm_bss_action_frm:
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) WNM_BSS_ACTION_FRAME,
WNM_BSS_ACTION_FRAME_SIZE);
dereg_gas_comeback_rsp:
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_RSP,
GAS_COMEBACK_RSP_SIZE);
dereg_gas_comeback_req:
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_REQ,
GAS_COMEBACK_REQ_SIZE);
dereg_gas_initial_rsp:
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_RSP,
GAS_INITIAL_RSP_SIZE);
dereg_gas_initial_req:
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_REQ,
GAS_INITIAL_REQ_SIZE);
ret_status:
return qdf_status_to_os_return(status);
}
void wlan_hdd_cfg80211_deregister_frames(struct hdd_adapter *adapter)
{
mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter);
/* Deregister for all P2P action, public action etc frames */
uint16_t type = (SIR_MAC_MGMT_FRAME << 2) | (SIR_MAC_MGMT_ACTION << 4);
hdd_enter();
/* Right now we are registering these frame when driver is getting
* initialized. Once we will move to 2.6.37 kernel, in which we have
* frame register ops, we will move this code as a part of that
*/
/* GAS Initial Request */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_REQ,
GAS_INITIAL_REQ_SIZE);
/* GAS Initial Response */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_INITIAL_RSP,
GAS_INITIAL_RSP_SIZE);
/* GAS Comeback Request */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_REQ,
GAS_COMEBACK_REQ_SIZE);
/* GAS Comeback Response */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) GAS_COMEBACK_RSP,
GAS_COMEBACK_RSP_SIZE);
/* P2P Public Action */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) P2P_PUBLIC_ACTION_FRAME,
P2P_PUBLIC_ACTION_FRAME_SIZE);
/* P2P Action */
sme_deregister_mgmt_frame(mac_handle, SME_SESSION_ID_ANY, type,
(uint8_t *) P2P_ACTION_FRAME,
P2P_ACTION_FRAME_SIZE);
/* WNM-Notification */
sme_deregister_mgmt_frame(mac_handle, adapter->vdev_id, type,
(uint8_t *) WNM_NOTIFICATION_FRAME,
WNM_NOTIFICATION_FRAME_SIZE);
}
#if defined(FEATURE_WLAN_WAPI) && !defined(CRYPTO_SET_KEY_CONVERGED)
static void wlan_hdd_cfg80211_set_key_wapi(struct hdd_adapter *adapter,
uint8_t key_index,
const uint8_t *mac_addr,
const uint8_t *key,
int key_len)
{
tCsrRoamSetKey set_key;
QDF_STATUS status;
uint32_t roam_id = INVALID_ROAM_ID;
mac_handle_t mac_handle;
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
qdf_mem_zero(&set_key, sizeof(set_key));
set_key.keyId = key_index;
set_key.encType = eCSR_ENCRYPT_TYPE_WPI;
set_key.keyDirection = eSIR_TX_RX;
set_key.paeRole = 0;
if (!mac_addr || is_broadcast_ether_addr(mac_addr))
qdf_set_macaddr_broadcast(&set_key.peerMac);
else
qdf_mem_copy(set_key.peerMac.bytes, mac_addr,
QDF_MAC_ADDR_SIZE);
set_key.keyLength = key_len;
memcpy(set_key.Key, key, key_len);
hdd_debug("WAPI KEY LENGTH:0x%04x", key_len);
mac_handle = hdd_adapter_get_mac_handle(adapter);
status = sme_roam_set_key(mac_handle, adapter->vdev_id,
&set_key, &roam_id);
if (status != QDF_STATUS_SUCCESS)
hdd_err("sme_roam_set_key failed status: %d", status);
}
#endif /* FEATURE_WLAN_WAPI */
bool wlan_hdd_is_ap_supports_immediate_power_save(uint8_t *ies, int length)
{
const uint8_t *vendor_ie;
if (length < 2) {
hdd_debug("bss size is less than expected");
return true;
}
if (!ies) {
hdd_debug("invalid IE pointer");
return true;
}
vendor_ie = wlan_get_vendor_ie_ptr_from_oui(VENDOR1_AP_OUI_TYPE,
VENDOR1_AP_OUI_TYPE_SIZE, ies, length);
if (vendor_ie) {
hdd_debug("AP can't support immediate powersave. defer it");
return false;
}
return true;
}
/*
* FUNCTION: wlan_hdd_validate_operation_channel
* called by wlan_hdd_cfg80211_start_bss() and
* wlan_hdd_set_channel()
* This function validates whether given channel is part of valid
* channel list.
*/
QDF_STATUS wlan_hdd_validate_operation_channel(struct hdd_adapter *adapter,
int channel)
{
uint32_t num_ch = 0;
u8 valid_ch[CFG_VALID_CHANNEL_LIST_LEN];
u32 indx = 0;
bool is_valid_channel = false;
uint8_t count;
QDF_STATUS status;
bool value;
struct hdd_context *hdd_ctx;
num_ch = CFG_VALID_CHANNEL_LIST_LEN;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = ucfg_mlme_get_sap_allow_all_channels(hdd_ctx->psoc, &value);
if (status != QDF_STATUS_SUCCESS)
hdd_err("Unable to fetch sap allow all channels");
if (value) {
/* Validate the channel */
for (count = CHAN_ENUM_1; count <= CHAN_ENUM_173; count++) {
if (channel == WLAN_REG_CH_NUM(count)) {
is_valid_channel = true;
break;
}
}
if (!is_valid_channel) {
hdd_err("Invalid Channel: %d", channel);
return QDF_STATUS_E_FAILURE;
}
} else {
ucfg_mlme_get_valid_channel_list(hdd_ctx->psoc, valid_ch,
&num_ch);
for (indx = 0; indx < num_ch; indx++) {
if (channel == valid_ch[indx])
break;
}
if (indx >= num_ch) {
hdd_err("Invalid Channel: %d", channel);
return QDF_STATUS_E_FAILURE;
}
}
return QDF_STATUS_SUCCESS;
}
static int __wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int ret = 0;
QDF_STATUS qdf_ret_status;
mac_handle_t mac_handle;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CHANGE_BSS,
adapter->vdev_id, params->ap_isolate);
hdd_debug("Device_mode %s(%d), ap_isolate = %d",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode, params->ap_isolate);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (!(adapter->device_mode == QDF_SAP_MODE ||
adapter->device_mode == QDF_P2P_GO_MODE)) {
return -EOPNOTSUPP;
}
/* ap_isolate == -1 means that in change bss, upper layer doesn't
* want to update this parameter
*/
if (-1 != params->ap_isolate) {
adapter->session.ap.disable_intrabss_fwd =
!!params->ap_isolate;
mac_handle = hdd_ctx->mac_handle;
qdf_ret_status = sme_ap_disable_intra_bss_fwd(mac_handle,
adapter->vdev_id,
adapter->session.
ap.
disable_intrabss_fwd);
if (!QDF_IS_STATUS_SUCCESS(qdf_ret_status))
ret = -EINVAL;
ucfg_ipa_set_ap_ibss_fwd(hdd_ctx->pdev,
adapter->session.ap.
disable_intrabss_fwd);
}
hdd_exit();
return ret;
}
/**
* hdd_change_adapter_mode() - change @adapter's operating mode to @new_mode
* @adapter: the adapter to change modes on
* @new_mode: the new operating mode to change to
*
* Return: Errno
*/
static int hdd_change_adapter_mode(struct hdd_adapter *adapter,
enum QDF_OPMODE new_mode)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct net_device *netdev = adapter->dev;
struct hdd_config *config = hdd_ctx->config;
struct csr_roam_profile *roam_profile;
QDF_STATUS status = QDF_STATUS_SUCCESS;
hdd_enter();
if (test_bit(ACS_IN_PROGRESS, &hdd_ctx->g_event_flags)) {
hdd_warn("Can't change interface: ACS in progress");
return -EBUSY;
}
hdd_stop_adapter(hdd_ctx, adapter);
hdd_deinit_adapter(hdd_ctx, adapter, true);
adapter->device_mode = new_mode;
memset(&adapter->session, 0, sizeof(adapter->session));
hdd_set_station_ops(netdev);
roam_profile = hdd_roam_profile(adapter);
roam_profile->pAddIEScan = adapter->scan_info.scan_add_ie.addIEdata;
roam_profile->nAddIEScanLength = adapter->scan_info.scan_add_ie.length;
if (new_mode == QDF_IBSS_MODE) {
status = hdd_start_station_adapter(adapter);
roam_profile->BSSType = eCSR_BSS_TYPE_START_IBSS;
roam_profile->phyMode =
hdd_cfg_xlate_to_csr_phy_mode(config->dot11Mode);
}
hdd_exit();
return qdf_status_to_os_return(status);
}
static int wlan_hdd_cfg80211_change_bss(struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_change_bss(wiphy, dev, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
static bool hdd_is_client_mode(enum QDF_OPMODE mode)
{
switch (mode) {
case QDF_STA_MODE:
case QDF_P2P_CLIENT_MODE:
case QDF_P2P_DEVICE_MODE:
case QDF_IBSS_MODE:
return true;
default:
return false;
}
}
static bool hdd_is_ap_mode(enum QDF_OPMODE mode)
{
switch (mode) {
case QDF_SAP_MODE:
case QDF_P2P_GO_MODE:
return true;
default:
return false;
}
}
/**
* __wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op
* @wiphy: Pointer to the wiphy structure
* @ndev: Pointer to the net device
* @type: Interface type
* @flags: Flags for change interface
* @params: Pointer to change interface parameters
*
* Return: 0 for success, error number on failure.
*/
static int __wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx;
bool iff_up = ndev->flags & IFF_UP;
enum QDF_OPMODE new_mode;
bool ap_random_bssid_enabled;
QDF_STATUS status;
int errno;
hdd_enter();
if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CHANGE_IFACE,
adapter->vdev_id, type);
status = hdd_nl_to_qdf_iface_type(type, &new_mode);
if (QDF_IS_STATUS_ERROR(status))
return qdf_status_to_os_return(status);
/* A userspace issue leads to it sending a 'change to station mode'
* request on a "p2p" device, expecting the driver do execute a 'change
* to p2p-device mode' request instead. The (unfortunate) work around
* here is implemented by overriding the new mode if the net_device name
* starts with "p2p" and the requested mode was station.
*/
if (strnstr(ndev->name, "p2p", 3) && new_mode == QDF_STA_MODE)
new_mode = QDF_P2P_DEVICE_MODE;
hdd_debug("Changing mode for '%s' from %s to %s",
ndev->name,
qdf_opmode_str(adapter->device_mode),
qdf_opmode_str(new_mode));
errno = hdd_psoc_idle_restart(hdd_ctx);
if (errno) {
hdd_err("Failed to restart psoc; errno:%d", errno);
return -EINVAL;
}
/* Reset the current device mode bit mask */
policy_mgr_clear_concurrency_mode(hdd_ctx->psoc, adapter->device_mode);
if (hdd_is_client_mode(adapter->device_mode)) {
if (hdd_is_client_mode(new_mode)) {
if (new_mode == QDF_IBSS_MODE) {
hdd_deregister_hl_netdev_fc_timer(adapter);
hdd_deregister_tx_flow_control(adapter);
}
errno = hdd_change_adapter_mode(adapter, new_mode);
if (errno) {
hdd_err("change intf mode fail %d", errno);
goto err;
}
} else if (hdd_is_ap_mode(new_mode)) {
if (new_mode == QDF_P2P_GO_MODE)
wlan_hdd_cancel_existing_remain_on_channel
(adapter);
hdd_stop_adapter(hdd_ctx, adapter);
hdd_deinit_adapter(hdd_ctx, adapter, true);
memset(&adapter->session, 0, sizeof(adapter->session));
adapter->device_mode = new_mode;
status = ucfg_mlme_get_ap_random_bssid_enable(
hdd_ctx->psoc,
&ap_random_bssid_enabled);
if (QDF_IS_STATUS_ERROR(status))
return qdf_status_to_os_return(status);
if (adapter->device_mode == QDF_SAP_MODE &&
ap_random_bssid_enabled) {
/* To meet Android requirements create
* a randomized MAC address of the
* form 02:1A:11:Fx:xx:xx
*/
get_random_bytes(&ndev->dev_addr[3], 3);
ndev->dev_addr[0] = 0x02;
ndev->dev_addr[1] = 0x1A;
ndev->dev_addr[2] = 0x11;
ndev->dev_addr[3] |= 0xF0;
memcpy(adapter->mac_addr.bytes, ndev->dev_addr,
QDF_MAC_ADDR_SIZE);
pr_info("wlan: Generated HotSpot BSSID "
QDF_MAC_ADDR_STR "\n",
QDF_MAC_ADDR_ARRAY(ndev->dev_addr));
}
hdd_set_ap_ops(adapter->dev);
} else {
hdd_err("Changing to device mode '%s' is not supported",
qdf_opmode_str(new_mode));
errno = -EOPNOTSUPP;
goto err;
}
} else if (hdd_is_ap_mode(adapter->device_mode)) {
if (hdd_is_client_mode(new_mode)) {
errno = hdd_change_adapter_mode(adapter, new_mode);
if (errno) {
hdd_err("change mode fail %d", errno);
goto err;
}
} else if (hdd_is_ap_mode(new_mode)) {
adapter->device_mode = new_mode;
/* avoid starting the adapter, since it never stopped */
iff_up = false;
} else {
hdd_err("Changing to device mode '%s' is not supported",
qdf_opmode_str(new_mode));
errno = -EOPNOTSUPP;
goto err;
}
} else {
hdd_err("Changing from device mode '%s' is not supported",
qdf_opmode_str(adapter->device_mode));
errno = -EOPNOTSUPP;
goto err;
}
/* restart the adapter if it was up before the change iface request */
if (iff_up) {
errno = hdd_start_adapter(adapter);
if (errno) {
hdd_err("Failed to start adapter");
errno = -EINVAL;
goto err;
}
}
ndev->ieee80211_ptr->iftype = type;
hdd_lpass_notify_mode_change(adapter);
err:
/* Set bitmask based on updated value */
policy_mgr_set_concurrency_mode(hdd_ctx->psoc, adapter->device_mode);
hdd_exit();
return errno;
}
static int _wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *net_dev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_trans_start(net_dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_change_iface(wiphy, net_dev, type,
flags, params);
osif_vdev_sync_trans_stop(vdev_sync);
return errno;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
/**
* wlan_hdd_cfg80211_change_iface() - change interface cfg80211 op
* @wiphy: Pointer to the wiphy structure
* @ndev: Pointer to the net device
* @type: Interface type
* @flags: Flags for change interface
* @params: Pointer to change interface parameters
*
* Return: 0 for success, error number on failure.
*/
static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type,
flags, params);
}
#else
static int wlan_hdd_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
struct vif_params *params)
{
return _wlan_hdd_cfg80211_change_iface(wiphy, ndev, type,
&params->flags, params);
}
#endif /* KERNEL_VERSION(4, 12, 0) */
QDF_STATUS wlan_hdd_send_sta_authorized_event(
struct hdd_adapter *adapter,
struct hdd_context *hdd_ctx,
const struct qdf_mac_addr *mac_addr)
{
struct sk_buff *vendor_event;
QDF_STATUS status;
struct nl80211_sta_flag_update sta_flags;
hdd_enter();
if (!hdd_ctx) {
hdd_err("HDD context is null");
return QDF_STATUS_E_INVAL;
}
vendor_event =
cfg80211_vendor_event_alloc(
hdd_ctx->wiphy, &adapter->wdev, sizeof(sta_flags) +
QDF_MAC_ADDR_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hdd_err("cfg80211_vendor_event_alloc failed");
return QDF_STATUS_E_FAILURE;
}
sta_flags.mask |= BIT(NL80211_STA_FLAG_AUTHORIZED);
sta_flags.set = true;
status = nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS,
sizeof(struct nl80211_sta_flag_update),
&sta_flags);
if (status) {
hdd_err("STA flag put fails");
kfree_skb(vendor_event);
return QDF_STATUS_E_FAILURE;
}
status = nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR,
QDF_MAC_ADDR_SIZE, mac_addr->bytes);
if (status) {
hdd_err("STA MAC put fails");
kfree_skb(vendor_event);
return QDF_STATUS_E_FAILURE;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
hdd_exit();
return QDF_STATUS_SUCCESS;
}
/**
* __wlan_hdd_change_station() - change station
* @wiphy: Pointer to the wiphy structure
* @dev: Pointer to the net device.
* @mac: bssid
* @params: Pointer to station parameters
*
* Return: 0 for success, error number on failure.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
static int __wlan_hdd_change_station(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *mac,
struct station_parameters *params)
#else
static int __wlan_hdd_change_station(struct wiphy *wiphy,
struct net_device *dev,
uint8_t *mac,
struct station_parameters *params)
#endif
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx;
struct hdd_station_ctx *sta_ctx;
struct hdd_ap_ctx *ap_ctx;
struct qdf_mac_addr sta_macaddr;
int ret;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CHANGE_STATION,
adapter->vdev_id, params->listen_interval);
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
qdf_mem_copy(sta_macaddr.bytes, mac, QDF_MAC_ADDR_SIZE);
if ((adapter->device_mode == QDF_SAP_MODE) ||
(adapter->device_mode == QDF_P2P_GO_MODE)) {
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter);
/*
* For Encrypted SAP session, this will be done as
* part of eSAP_STA_SET_KEY_EVENT
*/
if (ap_ctx->encryption_type !=
eCSR_ENCRYPT_TYPE_NONE) {
hdd_debug("Encrypt type %d, not setting peer authorized now",
ap_ctx->encryption_type);
return 0;
}
status =
hdd_softap_change_sta_state(adapter,
&sta_macaddr,
OL_TXRX_PEER_STATE_AUTH);
if (status != QDF_STATUS_SUCCESS) {
hdd_debug("Not able to change TL state to AUTHENTICATED");
return -EINVAL;
}
status = wlan_hdd_send_sta_authorized_event(
adapter,
hdd_ctx,
&sta_macaddr);
if (status != QDF_STATUS_SUCCESS) {
return -EINVAL;
}
}
} else if ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
#if defined(FEATURE_WLAN_TDLS)
struct wlan_objmgr_vdev *vdev;
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
ret = wlan_cfg80211_tdls_update_peer(vdev, mac, params);
hdd_objmgr_put_vdev(vdev);
#endif
}
}
hdd_exit();
return ret;
}
/**
* wlan_hdd_change_station() - cfg80211 change station handler function
* @wiphy: Pointer to the wiphy structure
* @dev: Pointer to the net device.
* @mac: bssid
* @params: Pointer to station parameters
*
* This is the cfg80211 change station handler function which invokes
* the internal function @__wlan_hdd_change_station with
* SSR protection.
*
* Return: 0 for success, error number on failure.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) || defined(WITH_BACKPORTS)
static int wlan_hdd_change_station(struct wiphy *wiphy,
struct net_device *dev,
const u8 *mac,
struct station_parameters *params)
#else
static int wlan_hdd_change_station(struct wiphy *wiphy,
struct net_device *dev,
u8 *mac,
struct station_parameters *params)
#endif
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_change_station(wiphy, dev, mac, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifdef CRYPTO_SET_KEY_CONVERGED
#ifdef FEATURE_WLAN_ESE
static bool hdd_is_krk_enc_type(uint32_t cipher_type)
{
if (cipher_type == WLAN_CIPHER_SUITE_KRK)
return true;
return false;
}
#else
static bool hdd_is_krk_enc_type(uint32_t cipher_type)
{
return false;
}
#endif
#if defined(FEATURE_WLAN_ESE) && defined(WLAN_FEATURE_ROAM_OFFLOAD)
static bool hdd_is_btk_enc_type(uint32_t cipher_type)
{
if (cipher_type == WLAN_CIPHER_SUITE_BTK)
return true;
return false;
}
#else
static bool hdd_is_btk_enc_type(uint32_t cipher_type)
{
return false;
}
#endif
#endif
#ifdef CRYPTO_SET_KEY_CONVERGED
static int wlan_hdd_add_key_ibss(struct hdd_adapter *adapter,
bool pairwise, u8 key_index,
const u8 *mac_addr, struct key_params *params,
bool *key_already_installed)
{
struct wlan_objmgr_vdev *vdev;
int errno;
if (pairwise)
return 0;
/* if a key is already installed, block all subsequent ones */
if (adapter->session.station.ibss_enc_key_installed) {
hdd_debug("IBSS key installed already");
*key_already_installed = true;
return 0;
}
/*Set the group key */
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
errno = wlan_cfg80211_crypto_add_key(vdev, WLAN_CRYPTO_KEY_TYPE_GROUP,
key_index);
if (errno) {
hdd_err("add_ibss_key failed, errno: %d", errno);
hdd_objmgr_put_vdev(vdev);
return errno;
}
/* Save the keys here and call set_key for setting
* the PTK after peer joins the IBSS network
*/
wlan_cfg80211_store_key(vdev, key_index, WLAN_CRYPTO_KEY_TYPE_UNICAST,
mac_addr, params);
hdd_objmgr_put_vdev(vdev);
adapter->session.station.ibss_enc_key_installed = 1;
return 0;
}
static int wlan_hdd_add_key_sap(struct hdd_adapter *adapter,
bool pairwise, u8 key_index,
enum wlan_crypto_cipher_type cipher)
{
struct wlan_objmgr_vdev *vdev;
int errno = 0;
struct hdd_hostapd_state *hostapd_state =
WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter);
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
if (hostapd_state->bss_state == BSS_START) {
errno =
wlan_cfg80211_crypto_add_key(vdev, (pairwise ?
WLAN_CRYPTO_KEY_TYPE_UNICAST :
WLAN_CRYPTO_KEY_TYPE_GROUP),
key_index);
if (!errno)
wma_update_set_key(adapter->vdev_id, pairwise,
key_index, cipher);
}
hdd_objmgr_put_vdev(vdev);
return errno;
}
static int wlan_hdd_add_key_sta(struct hdd_adapter *adapter,
bool pairwise, u8 key_index,
mac_handle_t mac_handle, bool *ft_mode)
{
struct wlan_objmgr_vdev *vdev;
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
int errno;
QDF_STATUS status;
if (!pairwise) {
/* set group key */
if (sta_ctx->roam_info.defer_key_complete) {
hdd_debug("Perform Set key Complete");
hdd_perform_roam_set_key_complete(adapter);
}
}
/* The supplicant may attempt to set the PTK once
* pre-authentication is done. Save the key in the
* UMAC and include it in the ADD BSS request
*/
status = sme_check_ft_status(mac_handle, adapter->vdev_id);
if (status == QDF_STATUS_SUCCESS) {
*ft_mode = true;
return 0;
}
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
errno = wlan_cfg80211_crypto_add_key(vdev, (pairwise ?
WLAN_CRYPTO_KEY_TYPE_UNICAST :
WLAN_CRYPTO_KEY_TYPE_GROUP),
key_index);
hdd_objmgr_put_vdev(vdev);
if (!errno && adapter->send_mode_change) {
wlan_hdd_send_mode_change_event();
adapter->send_mode_change = false;
}
return errno;
}
static int __wlan_hdd_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
struct hdd_context *hdd_ctx;
mac_handle_t mac_handle;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct wlan_objmgr_vdev *vdev;
bool key_already_installed = false, ft_mode = false;
enum wlan_crypto_cipher_type cipher;
int errno;
struct qdf_mac_addr mac_address;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_KEY,
adapter->vdev_id, params->key_len);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
hdd_debug("converged Device_mode %s(%d) index %d, pairwise %d",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode, key_index, pairwise);
mac_handle = hdd_ctx->mac_handle;
if (hdd_is_btk_enc_type(params->cipher))
return sme_add_key_btk(mac_handle, adapter->vdev_id,
params->key, params->key_len);
if (hdd_is_krk_enc_type(params->cipher))
return sme_add_key_krk(mac_handle, adapter->vdev_id,
params->key, params->key_len);
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
if (!pairwise && ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE))) {
qdf_mem_copy(mac_address.bytes,
adapter->session.station.conn_info.bssid.bytes,
QDF_MAC_ADDR_SIZE);
} else {
if (mac_addr)
qdf_mem_copy(mac_address.bytes, mac_addr,
QDF_MAC_ADDR_SIZE);
}
errno = wlan_cfg80211_store_key(vdev, key_index,
(pairwise ?
WLAN_CRYPTO_KEY_TYPE_UNICAST :
WLAN_CRYPTO_KEY_TYPE_GROUP),
mac_address.bytes, params);
hdd_objmgr_put_vdev(vdev);
if (errno)
return errno;
cipher = osif_nl_to_crypto_cipher_type(params->cipher);
if (pairwise)
wma_set_peer_ucast_cipher(mac_address.bytes, cipher);
switch (adapter->device_mode) {
case QDF_IBSS_MODE:
errno = wlan_hdd_add_key_ibss(adapter, pairwise, key_index,
mac_addr, params,
&key_already_installed);
if (key_already_installed)
return 0;
break;
case QDF_SAP_MODE:
case QDF_P2P_GO_MODE:
errno = wlan_hdd_add_key_sap(adapter, pairwise,
key_index, cipher);
break;
case QDF_STA_MODE:
case QDF_P2P_CLIENT_MODE:
errno = wlan_hdd_add_key_sta(adapter, pairwise, key_index,
mac_handle, &ft_mode);
if (ft_mode)
return 0;
break;
default:
break;
}
if (!errno && (adapter->device_mode != QDF_SAP_MODE))
wma_update_set_key(adapter->vdev_id, pairwise, key_index,
cipher);
hdd_exit();
return errno;
}
#else /* !CRYPTO_SET_KEY_CONVERGED */
/*
* FUNCTION: __wlan_hdd_cfg80211_add_key
* This function is used to initialize the key information
*/
static int __wlan_hdd_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
tCsrRoamSetKey set_key;
int errno;
uint32_t roam_id = INVALID_ROAM_ID;
QDF_STATUS status;
struct hdd_context *hdd_ctx;
mac_handle_t mac_handle;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_KEY,
adapter->vdev_id, params->key_len);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
if (CSR_MAX_NUM_KEY <= key_index) {
hdd_err("Invalid key index %d", key_index);
return -EINVAL;
}
if (CSR_MAX_KEY_LEN < params->key_len) {
hdd_err("Invalid key length %d", params->key_len);
return -EINVAL;
}
if (WLAN_CRYPTO_RSC_SIZE < params->seq_len) {
hdd_err("Invalid seq length %d", params->seq_len);
return -EINVAL;
}
hdd_debug("key index %d, key length %d, seq length %d",
key_index, params->key_len, params->seq_len);
/*extract key idx, key len and key */
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
set_key.keyId = key_index;
set_key.keyLength = params->key_len;
qdf_mem_copy(&set_key.Key[0], params->key, params->key_len);
qdf_mem_copy(&set_key.keyRsc[0], params->seq, params->seq_len);
mac_handle = hdd_ctx->mac_handle;
switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
set_key.encType = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY;
break;
case WLAN_CIPHER_SUITE_WEP104:
set_key.encType = eCSR_ENCRYPT_TYPE_WEP104_STATICKEY;
break;
case WLAN_CIPHER_SUITE_TKIP:
{
u8 *key = &set_key.Key[0];
set_key.encType = eCSR_ENCRYPT_TYPE_TKIP;
qdf_mem_zero(key, CSR_MAX_KEY_LEN);
/* Supplicant sends the 32bytes key in this order
*
* |--------------|----------|----------|
* | Tk1 |TX-MIC | RX Mic |
* |--------------|----------|----------|
* <---16bytes---><--8bytes--><--8bytes-->
*
* Sme expects the 32 bytes key to be in the below order
*
* |--------------|----------|----------|
* | Tk1 |RX-MIC | TX Mic |
* |--------------|----------|----------|
* <---16bytes---><--8bytes--><--8bytes-->
*/
/* Copy the Temporal Key 1 (TK1) */
qdf_mem_copy(key, params->key, 16);
/*Copy the rx mic first */
qdf_mem_copy(&key[16], &params->key[24], 8);
/*Copy the tx mic */
qdf_mem_copy(&key[24], &params->key[16], 8);
break;
}
case WLAN_CIPHER_SUITE_CCMP:
set_key.encType = eCSR_ENCRYPT_TYPE_AES;
break;
#ifdef FEATURE_WLAN_WAPI
case WLAN_CIPHER_SUITE_SMS4:
{
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
wlan_hdd_cfg80211_set_key_wapi(adapter, key_index,
mac_addr, params->key,
params->key_len);
return 0;
}
#endif
#ifdef FEATURE_WLAN_ESE
case WLAN_CIPHER_SUITE_KRK:
set_key.encType = eCSR_ENCRYPT_TYPE_KRK;
break;
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
case WLAN_CIPHER_SUITE_BTK:
set_key.encType = eCSR_ENCRYPT_TYPE_BTK;
break;
#endif
#endif
#ifdef WLAN_FEATURE_11W
case WLAN_CIPHER_SUITE_AES_CMAC:
set_key.encType = eCSR_ENCRYPT_TYPE_AES_CMAC;
break;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
set_key.encType = eCSR_ENCRYPT_TYPE_AES_GMAC_128;
break;
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
set_key.encType = eCSR_ENCRYPT_TYPE_AES_GMAC_256;
break;
#endif
#endif
case WLAN_CIPHER_SUITE_GCMP:
set_key.encType = eCSR_ENCRYPT_TYPE_AES_GCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
set_key.encType = eCSR_ENCRYPT_TYPE_AES_GCMP_256;
break;
default:
hdd_err("Unsupported cipher type: %u", params->cipher);
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return -EOPNOTSUPP;
}
hdd_debug("encryption type %d", set_key.encType);
if (!pairwise) {
/* set group key */
hdd_debug("setting Broadcast key");
set_key.keyDirection = eSIR_RX_ONLY;
qdf_set_macaddr_broadcast(&set_key.peerMac);
} else {
/* set pairwise key */
hdd_debug("setting pairwise key");
set_key.keyDirection = eSIR_TX_RX;
qdf_mem_copy(set_key.peerMac.bytes, mac_addr, QDF_MAC_ADDR_SIZE);
}
if ((QDF_IBSS_MODE == adapter->device_mode) && !pairwise) {
/* if a key is already installed, block all subsequent ones */
if (adapter->session.station.ibss_enc_key_installed) {
hdd_debug("IBSS key installed already");
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return 0;
}
set_key.keyDirection = eSIR_TX_RX;
/*Set the group key */
status = sme_roam_set_key(mac_handle,
adapter->vdev_id, &set_key, &roam_id);
if (0 != status) {
hdd_err("sme_roam_set_key failed, status: %d", status);
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return -EINVAL;
}
/* Save the keys here and call sme_roam_set_key for setting
* the PTK after peer joins the IBSS network
*/
qdf_mem_copy(&adapter->session.station.ibss_enc_key,
&set_key, sizeof(tCsrRoamSetKey));
adapter->session.station.ibss_enc_key_installed = 1;
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return qdf_status_to_os_return(status);
}
if ((adapter->device_mode == QDF_SAP_MODE) ||
(adapter->device_mode == QDF_P2P_GO_MODE)) {
struct hdd_hostapd_state *hostapd_state =
WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter);
struct hdd_ap_ctx *ap_ctx =
WLAN_HDD_GET_AP_CTX_PTR(adapter);
if (hostapd_state->bss_state == BSS_START) {
status = wlansap_set_key_sta(
WLAN_HDD_GET_SAP_CTX_PTR(adapter), &set_key);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("wlansap_set_key_sta failed status: %d",
status);
}
}
/* Save the key in ap ctx for use on START_BSS and restart */
if (pairwise ||
eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == set_key.encType ||
eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == set_key.encType)
qdf_mem_copy(&ap_ctx->wep_key[key_index], &set_key,
sizeof(tCsrRoamSetKey));
else
qdf_mem_copy(&ap_ctx->group_key, &set_key,
sizeof(tCsrRoamSetKey));
} else if ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct csr_roam_profile *roam_profile;
if (!pairwise) {
/* set group key */
if (sta_ctx->roam_info.defer_key_complete) {
hdd_debug("Perform Set key Complete");
hdd_perform_roam_set_key_complete(adapter);
}
}
roam_profile = hdd_roam_profile(adapter);
roam_profile->Keys.KeyLength[key_index] = params->key_len;
roam_profile->Keys.defaultIndex = key_index;
qdf_mem_copy(&roam_profile->Keys.KeyMaterial[key_index][0],
params->key, params->key_len);
hdd_debug("Set key for peerMac "QDF_MAC_ADDR_STR" direction %d",
QDF_MAC_ADDR_ARRAY(set_key.peerMac.bytes),
set_key.keyDirection);
/* The supplicant may attempt to set the PTK once
* pre-authentication is done. Save the key in the
* UMAC and include it in the ADD BSS request
*/
status = sme_ft_update_key(mac_handle,
adapter->vdev_id, &set_key);
if (status == QDF_STATUS_FT_PREAUTH_KEY_SUCCESS) {
hdd_debug("Update PreAuth Key success");
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return 0;
} else if (status == QDF_STATUS_FT_PREAUTH_KEY_FAILED) {
hdd_err("Update PreAuth Key failed");
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return -EINVAL;
}
/* issue set key request to SME */
status = sme_roam_set_key(mac_handle,
adapter->vdev_id, &set_key,
&roam_id);
if (0 != status) {
hdd_err("sme_roam_set_key failed, status: %d", status);
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return -EINVAL;
}
if (adapter->send_mode_change) {
wlan_hdd_send_mode_change_event();
adapter->send_mode_change = false;
}
/* in case of IBSS as there was no information
* available about WEP keys during IBSS join, group
* key initialized with NULL key, so re-initialize
* group key with correct value
*/
if ((eCSR_BSS_TYPE_START_IBSS == roam_profile->BSSType) &&
!((HDD_AUTH_KEY_MGMT_802_1X ==
(sta_ctx->auth_key_mgmt & HDD_AUTH_KEY_MGMT_802_1X))
&& (eCSR_AUTH_TYPE_OPEN_SYSTEM ==
sta_ctx->conn_info.auth_type)
)
&& ((WLAN_CIPHER_SUITE_WEP40 == params->cipher)
|| (WLAN_CIPHER_SUITE_WEP104 == params->cipher)
)
) {
set_key.keyDirection = eSIR_RX_ONLY;
qdf_set_macaddr_broadcast(&set_key.peerMac);
hdd_debug("Set key peerMac "QDF_MAC_ADDR_STR" direction %d",
QDF_MAC_ADDR_ARRAY(set_key.peerMac.bytes),
set_key.keyDirection);
status = sme_roam_set_key(mac_handle,
adapter->vdev_id, &set_key,
&roam_id);
if (0 != status) {
hdd_err("sme_roam_set_key failed for group key (IBSS), returned %d", status);
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
return -EINVAL;
}
}
}
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
hdd_exit();
return 0;
}
#endif /* CRYPTO_SET_KEY_CONVERGED */
static int wlan_hdd_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(ndev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_add_key(wiphy, ndev, key_index, pairwise,
mac_addr, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_get_key
* This function is used to get the key information
*/
static int __wlan_hdd_cfg80211_get_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params *)
)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct csr_roam_profile *roam_profile;
struct key_params params;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
memset(&params, 0, sizeof(params));
if (CSR_MAX_NUM_KEY <= key_index) {
hdd_err("Invalid key index: %d", key_index);
return -EINVAL;
}
if ((adapter->device_mode == QDF_SAP_MODE) ||
(adapter->device_mode == QDF_P2P_GO_MODE)) {
struct hdd_ap_ctx *ap_ctx =
WLAN_HDD_GET_AP_CTX_PTR(adapter);
roam_profile =
wlan_sap_get_roam_profile(ap_ctx->sap_context);
} else {
roam_profile = hdd_roam_profile(adapter);
}
if (!roam_profile) {
hdd_err("Get roam profile failed!");
return -EINVAL;
}
switch (roam_profile->EncryptionType.encryptionType[0]) {
case eCSR_ENCRYPT_TYPE_NONE:
params.cipher = IW_AUTH_CIPHER_NONE;
break;
case eCSR_ENCRYPT_TYPE_WEP40_STATICKEY:
case eCSR_ENCRYPT_TYPE_WEP40:
params.cipher = WLAN_CIPHER_SUITE_WEP40;
break;
case eCSR_ENCRYPT_TYPE_WEP104_STATICKEY:
case eCSR_ENCRYPT_TYPE_WEP104:
params.cipher = WLAN_CIPHER_SUITE_WEP104;
break;
case eCSR_ENCRYPT_TYPE_TKIP:
params.cipher = WLAN_CIPHER_SUITE_TKIP;
break;
case eCSR_ENCRYPT_TYPE_AES:
params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
break;
case eCSR_ENCRYPT_TYPE_AES_GCMP:
params.cipher = WLAN_CIPHER_SUITE_GCMP;
break;
case eCSR_ENCRYPT_TYPE_AES_GCMP_256:
params.cipher = WLAN_CIPHER_SUITE_GCMP_256;
break;
default:
params.cipher = IW_AUTH_CIPHER_NONE;
break;
}
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_KEY,
adapter->vdev_id, params.cipher);
params.key_len = roam_profile->Keys.KeyLength[key_index];
params.seq_len = 0;
params.seq = NULL;
params.key = &roam_profile->Keys.KeyMaterial[key_index][0];
callback(cookie, &params);
hdd_exit();
return 0;
}
static int wlan_hdd_cfg80211_get_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie,
struct key_params *)
)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(ndev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_get_key(wiphy, ndev, key_index, pairwise,
mac_addr, cookie, callback);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_del_key() - Delete the encryption key for station
* @wiphy: wiphy interface context
* @ndev: pointer to net device
* @key_index: Key index used in 802.11 frames
* @unicast: true if it is unicast key
* @multicast: true if it is multicast key
*
* This function is required for cfg80211_ops API.
* It is used to delete the key information
* Underlying hardware implementation does not have API to delete the
* encryption key. It is automatically deleted when the peer is
* removed. Hence this function currently does nothing.
* Future implementation may interprete delete key operation to
* replacing the key with a random junk value, effectively making it
* useless.
*
* Return: status code, always 0.
*/
static int __wlan_hdd_cfg80211_del_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool pairwise, const u8 *mac_addr)
{
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_del_key() - cfg80211 delete key handler function
* @wiphy: Pointer to wiphy structure.
* @dev: Pointer to net_device structure.
* @key_index: key index
* @pairwise: pairwise
* @mac_addr: mac address
*
* This is the cfg80211 delete key handler function which invokes
* the internal function @__wlan_hdd_cfg80211_del_key with
* SSR protection.
*
* Return: 0 for success, error number on failure.
*/
static int wlan_hdd_cfg80211_del_key(struct wiphy *wiphy,
struct net_device *dev,
u8 key_index,
bool pairwise, const u8 *mac_addr)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_del_key(wiphy, dev, key_index,
pairwise, mac_addr);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifndef CRYPTO_SET_KEY_CONVERGED
#ifdef FEATURE_WLAN_WAPI
static bool hdd_is_wapi_enc_type(eCsrEncryptionType encrypt_type)
{
if (encrypt_type == eCSR_ENCRYPT_TYPE_WPI)
return true;
return false;
}
#else
static bool hdd_is_wapi_enc_type(eCsrEncryptionType encrypt_type)
{
return false;
}
#endif
#endif
#ifdef CRYPTO_SET_KEY_CONVERGED
static int __wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool unicast, bool multicast)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx;
struct qdf_mac_addr bssid = QDF_MAC_ADDR_BCAST_INIT;
struct hdd_station_ctx *sta_ctx;
struct wlan_crypto_key *crypto_key;
int ret;
QDF_STATUS status;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY,
adapter->vdev_id, key_index);
hdd_debug("Device_mode %s(%d) key_index = %d",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode, key_index);
if (CSR_MAX_NUM_KEY <= key_index) {
hdd_err("Invalid key index: %d", key_index);
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
crypto_key = wlan_crypto_get_key(adapter->vdev, key_index);
hdd_debug("unicast %d, cipher %d", unicast, crypto_key->cipher_type);
if (!IS_WEP_CIPHER(crypto_key->cipher_type))
return 0;
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (unicast)
status =
wlan_cfg80211_set_default_key(adapter->vdev, key_index,
&sta_ctx->conn_info.bssid);
else
status = wlan_cfg80211_set_default_key(adapter->vdev, key_index,
&bssid);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("ret fail status %d", ret);
return -EINVAL;
}
if ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
ret =
wlan_cfg80211_crypto_add_key(adapter->vdev, (unicast ?
WLAN_CRYPTO_KEY_TYPE_UNICAST :
WLAN_CRYPTO_KEY_TYPE_GROUP),
key_index);
wma_update_set_key(adapter->vdev_id, unicast, key_index,
crypto_key->cipher_type);
}
return ret;
}
#else
/*
* FUNCTION: __wlan_hdd_cfg80211_set_default_key
* This function is used to set the default tx key index
*/
static int __wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool unicast, bool multicast)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx;
mac_handle_t mac_handle;
int status;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY,
adapter->vdev_id, key_index);
hdd_debug("Device_mode %s(%d) key_index = %d",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode, key_index);
if (CSR_MAX_NUM_KEY <= key_index) {
hdd_err("Invalid key index: %d", key_index);
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
mac_handle = hdd_ctx->mac_handle;
if ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct csr_roam_profile *roam_profile;
roam_profile = hdd_roam_profile(adapter);
if ((eCSR_ENCRYPT_TYPE_TKIP !=
sta_ctx->conn_info.uc_encrypt_type) &&
!hdd_is_wapi_enc_type(
sta_ctx->conn_info.uc_encrypt_type) &&
(eCSR_ENCRYPT_TYPE_AES !=
sta_ctx->conn_info.uc_encrypt_type) &&
(eCSR_ENCRYPT_TYPE_AES_GCMP !=
sta_ctx->conn_info.uc_encrypt_type) &&
(eCSR_ENCRYPT_TYPE_AES_GCMP_256 !=
sta_ctx->conn_info.uc_encrypt_type)) {
/* If default key index is not same as previous one,
* then update the default key index
*/
tCsrRoamSetKey set_key;
uint32_t roam_id = INVALID_ROAM_ID;
tCsrKeys *keys = &roam_profile->Keys;
hdd_debug("Default tx key index %d", key_index);
keys->defaultIndex = (u8) key_index;
qdf_mem_zero(&set_key, sizeof(tCsrRoamSetKey));
set_key.keyId = key_index;
set_key.keyLength = keys->KeyLength[key_index];
qdf_mem_copy(&set_key.Key[0],
&keys->KeyMaterial[key_index][0],
keys->KeyLength[key_index]);
set_key.keyDirection = eSIR_TX_RX;
qdf_copy_macaddr(&set_key.peerMac,
&sta_ctx->conn_info.bssid);
if (keys->KeyLength[key_index] ==
WLAN_CRYPTO_KEY_WEP40_LEN &&
roam_profile->EncryptionType.
encryptionType[0] == eCSR_ENCRYPT_TYPE_WEP104) {
/* In the case of dynamic wep
* supplicant hardcodes DWEP type to
* eCSR_ENCRYPT_TYPE_WEP104 even
* though ap is configured for WEP-40
* encryption. In this canse the key
* length is 5 but the encryption type
* is 104 hence checking the key
* length(5) and encryption type(104)
* and switching encryption type to 40
*/
roam_profile->EncryptionType.
encryptionType[0] = eCSR_ENCRYPT_TYPE_WEP40;
roam_profile->mcEncryptionType.
encryptionType[0] = eCSR_ENCRYPT_TYPE_WEP40;
}
set_key.encType =
roam_profile->EncryptionType.
encryptionType[0];
/* Issue set key request */
status = sme_roam_set_key(mac_handle,
adapter->vdev_id, &set_key,
&roam_id);
if (0 != status) {
hdd_err("sme_roam_set_key failed, status: %d",
status);
return -EINVAL;
}
}
} else if (QDF_SAP_MODE == adapter->device_mode) {
struct hdd_ap_ctx *ap_ctx =
WLAN_HDD_GET_AP_CTX_PTR(adapter);
struct csr_roam_profile *profile =
wlan_sap_get_roam_profile(ap_ctx->sap_context);
if (!profile) {
hdd_err("Failed to get SAP Roam Profile");
return -EINVAL;
}
/* In SoftAp mode setting key direction for default mode */
if ((eCSR_ENCRYPT_TYPE_TKIP !=
profile->EncryptionType.encryptionType[0]) &&
(eCSR_ENCRYPT_TYPE_AES !=
profile->EncryptionType.encryptionType[0]) &&
(eCSR_ENCRYPT_TYPE_AES_GCMP !=
profile->EncryptionType.encryptionType[0]) &&
(eCSR_ENCRYPT_TYPE_AES_GCMP_256 !=
profile->EncryptionType.encryptionType[0])) {
/* Saving key direction for default key index to TX default */
ap_ctx->wep_key[key_index].keyDirection =
eSIR_TX_DEFAULT;
hdd_debug("WEP default key index set to SAP context %d",
key_index);
ap_ctx->wep_def_key_idx = key_index;
}
}
hdd_exit();
return status;
}
#endif
static int wlan_hdd_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool unicast, bool multicast)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(ndev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_default_key(wiphy, ndev, key_index,
unicast, multicast);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
void wlan_hdd_cfg80211_unlink_bss(struct hdd_adapter *adapter,
tSirMacAddr bssid)
{
struct net_device *dev = adapter->dev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_bss *bss = NULL;
bss = wlan_cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0);
if (!bss) {
hdd_err("BSS not present");
} else {
hdd_debug("cfg80211_unlink_bss called for BSSID "
QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(bssid));
cfg80211_unlink_bss(wiphy, bss);
/* cfg80211_get_bss get bss with ref count so release it */
cfg80211_put_bss(wiphy, bss);
}
}
#ifdef WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS
static inline int
wlan_hdd_get_frame_len(struct bss_description *bss_desc)
{
return GET_IE_LEN_IN_BSS(bss_desc->length) + sizeof(qcom_ie_age);
}
static inline void wlan_hdd_add_age_ie(struct ieee80211_mgmt *mgmt,
uint32_t *ie_length, struct bss_description *bss_desc)
{
qcom_ie_age *qie_age = NULL;
/*
* GPS Requirement: need age ie per entry. Using vendor specific.
* Assuming this is the last IE, copy at the end
*/
*ie_length -= sizeof(qcom_ie_age);
qie_age = (qcom_ie_age *)(mgmt->u.probe_resp.variable + *ie_length);
qie_age->element_id = QCOM_VENDOR_IE_ID;
qie_age->len = QCOM_VENDOR_IE_AGE_LEN;
qie_age->oui_1 = QCOM_OUI1;
qie_age->oui_2 = QCOM_OUI2;
qie_age->oui_3 = QCOM_OUI3;
qie_age->type = QCOM_VENDOR_IE_AGE_TYPE;
/*
* Lowi expects the timestamp of bss in units of 1/10 ms. In driver
* all bss related timestamp is in units of ms. Due to this when scan
* results are sent to lowi the scan age is high.To address this,
* send age in units of 1/10 ms.
*/
qie_age->age = (uint32_t)(qdf_mc_timer_get_system_time() -
bss_desc->received_time)/10;
qie_age->tsf_delta = bss_desc->tsf_delta;
memcpy(&qie_age->beacon_tsf, bss_desc->timeStamp,
sizeof(qie_age->beacon_tsf));
memcpy(&qie_age->seq_ctrl, &bss_desc->seq_ctrl,
sizeof(qie_age->seq_ctrl));
}
#else
static inline int
wlan_hdd_get_frame_len(struct bss_description *bss_desc)
{
return GET_IE_LEN_IN_BSS(bss_desc->length);
}
static inline void wlan_hdd_add_age_ie(struct ieee80211_mgmt *mgmt,
uint32_t *ie_length, struct bss_description *bss_desc)
{
}
#endif /* WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS */
struct cfg80211_bss *
wlan_hdd_inform_bss_frame(struct hdd_adapter *adapter,
struct bss_description *bss_desc)
{
struct wireless_dev *wdev = adapter->dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
uint32_t ie_length = wlan_hdd_get_frame_len(bss_desc);
const char *ie =
((ie_length != 0) ? (const char *)&bss_desc->ieFields : NULL);
uint32_t freq, i;
struct cfg80211_bss *bss_status = NULL;
struct hdd_context *hdd_ctx;
struct timespec ts;
struct hdd_config *cfg_param;
struct wlan_cfg80211_inform_bss bss_data = {0};
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
/*
* wlan_hdd_validate_context should not be used here, In validate ctx
* start_modules_in_progress or stop_modules_in_progress is validated,
* If the start_modules_in_progress is set to true means the interface
* is not UP yet if the stop_modules_in_progress means that interface
* is already down. So in both the two scenario's driver should not be
* informing bss to kernel. Hence removing the validate context.
*/
if (!hdd_ctx || !hdd_ctx->config) {
hdd_debug("HDD context is Null");
return NULL;
}
if (cds_is_driver_recovering() ||
cds_is_load_or_unload_in_progress()) {
hdd_debug("Recovery or load/unload in progress. State: 0x%x",
cds_get_driver_state());
return NULL;
}
cfg_param = hdd_ctx->config;
bss_data.frame_len = ie_length + offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
bss_data.mgmt = qdf_mem_malloc(bss_data.frame_len);
if (!bss_data.mgmt)
return NULL;
memcpy(bss_data.mgmt->bssid, bss_desc->bssId, ETH_ALEN);
/* Android does not want the timestamp from the frame.
* Instead it wants a monotonic increasing value
*/
get_monotonic_boottime(&ts);
bss_data.mgmt->u.probe_resp.timestamp =
((u64) ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
bss_data.mgmt->u.probe_resp.beacon_int = bss_desc->beaconInterval;
bss_data.mgmt->u.probe_resp.capab_info = bss_desc->capabilityInfo;
wlan_hdd_add_age_ie(bss_data.mgmt, &ie_length, bss_desc);
memcpy(bss_data.mgmt->u.probe_resp.variable, ie, ie_length);
if (bss_desc->fProbeRsp) {
bss_data.mgmt->frame_control |=
(u16) (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
} else {
bss_data.mgmt->frame_control |=
(u16) (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
}
if (bss_desc->channelId <= ARRAY_SIZE(hdd_channels_2_4_ghz) &&
(wiphy->bands[HDD_NL80211_BAND_2GHZ])) {
freq =
ieee80211_channel_to_frequency(bss_desc->channelId,
HDD_NL80211_BAND_2GHZ);
} else if ((bss_desc->channelId > ARRAY_SIZE(hdd_channels_2_4_ghz))
&& (wiphy->bands[HDD_NL80211_BAND_5GHZ])) {
freq =
ieee80211_channel_to_frequency(bss_desc->channelId,
HDD_NL80211_BAND_5GHZ);
} else {
hdd_err("Invalid channel: %d", bss_desc->channelId);
qdf_mem_free(bss_data.mgmt);
return NULL;
}
bss_data.chan = ieee80211_get_channel(wiphy, freq);
if (!bss_data.chan) {
hdd_err("chan pointer is NULL, chan_no: %d freq: %d",
bss_desc->channelId, freq);
qdf_mem_free(bss_data.mgmt);
return NULL;
}
/*
* Based on .ini configuration, raw rssi can be reported for bss.
* Raw rssi is typically used for estimating power.
*/
bss_data.rssi = (cfg_param->inform_bss_rssi_raw) ? bss_desc->rssi_raw :
bss_desc->rssi;
/* Supplicant takes the signal strength in terms of mBm(100*dBm) */
bss_data.rssi = QDF_MIN(bss_data.rssi, 0) * 100;
bss_data.boottime_ns = bss_desc->scansystimensec;
/* Set all per chain rssi as invalid */
for (i = 0; i < WLAN_MGMT_TXRX_HOST_MAX_ANTENNA; i++)
bss_data.per_chain_rssi[i] = WLAN_INVALID_PER_CHAIN_RSSI;
hdd_debug("BSSID: " QDF_MAC_ADDR_STR " Channel:%d RSSI:%d TSF %u seq %d",
QDF_MAC_ADDR_ARRAY(bss_data.mgmt->bssid),
bss_data.chan->center_freq, (int)(bss_data.rssi / 100),
bss_desc->timeStamp[0], ((bss_desc->seq_ctrl.seqNumHi <<
HIGH_SEQ_NUM_OFFSET) | bss_desc->seq_ctrl.seqNumLo));
bss_status = wlan_cfg80211_inform_bss_frame_data(wiphy, &bss_data);
hdd_ctx->beacon_probe_rsp_cnt_per_scan++;
qdf_mem_free(bss_data.mgmt);
return bss_status;
}
/**
* wlan_hdd_cfg80211_update_bss_db() - update bss database of CF80211
* @adapter: Pointer to adapter
* @roam_info: Pointer to roam info
*
* This function is used to update the BSS data base of CFG8011
*
* Return: struct cfg80211_bss pointer
*/
struct cfg80211_bss *
wlan_hdd_cfg80211_update_bss_db(struct hdd_adapter *adapter,
struct csr_roam_info *roam_info)
{
tCsrRoamConnectedProfile roamProfile;
mac_handle_t mac_handle = hdd_adapter_get_mac_handle(adapter);
struct cfg80211_bss *bss = NULL;
memset(&roamProfile, 0, sizeof(tCsrRoamConnectedProfile));
sme_roam_get_connect_profile(mac_handle, adapter->vdev_id,
&roamProfile);
if (roamProfile.pBssDesc) {
bss = wlan_hdd_inform_bss_frame(adapter, roamProfile.pBssDesc);
if (!bss)
hdd_debug("wlan_hdd_inform_bss_frame returned NULL");
sme_roam_free_connect_profile(&roamProfile);
} else {
hdd_err("roamProfile.pBssDesc is NULL");
}
return bss;
}
/**
* wlan_hdd_cfg80211_pmksa_candidate_notify() - notify a new PMSKA candidate
* @adapter: Pointer to adapter
* @roam_info: Pointer to roam info
* @index: Index
* @preauth: Preauth flag
*
* This function is used to notify the supplicant of a new PMKSA candidate.
* PMK value is notified to supplicant whether PMK caching or OKC is enabled
* in firmware or not. Supplicant needs this value becaue it uses PMK caching
* by default.
*
* Return: 0 for success, non-zero for failure
*/
int wlan_hdd_cfg80211_pmksa_candidate_notify(struct hdd_adapter *adapter,
struct csr_roam_info *roam_info,
int index, bool preauth)
{
struct net_device *dev = adapter->dev;
hdd_enter();
hdd_debug("is going to notify supplicant of:");
if (!roam_info) {
hdd_err("roam_info is NULL");
return -EINVAL;
}
hdd_info(QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(roam_info->bssid.bytes));
cfg80211_pmksa_candidate_notify(dev, index,
roam_info->bssid.bytes,
preauth, GFP_KERNEL);
return 0;
}
#ifdef FEATURE_WLAN_LFR_METRICS
/**
* wlan_hdd_cfg80211_roam_metrics_preauth() - roam metrics preauth
* @adapter: Pointer to adapter
* @roam_info: Pointer to roam info
*
* 802.11r/LFR metrics reporting function to report preauth initiation
*
* Return: QDF status
*/
#define MAX_LFR_METRICS_EVENT_LENGTH 100
QDF_STATUS
wlan_hdd_cfg80211_roam_metrics_preauth(struct hdd_adapter *adapter,
struct csr_roam_info *roam_info)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
hdd_enter();
if (!adapter) {
hdd_err("adapter is NULL!");
return QDF_STATUS_E_FAILURE;
}
/* create the event */
memset(&wrqu, 0, sizeof(wrqu));
memset(metrics_notification, 0, sizeof(metrics_notification));
wrqu.data.pointer = metrics_notification;
wrqu.data.length = scnprintf(metrics_notification,
sizeof(metrics_notification),
"QCOM: LFR_PREAUTH_INIT " QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(roam_info->bssid.bytes));
wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu,
metrics_notification);
hdd_exit();
return QDF_STATUS_SUCCESS;
}
/**
* wlan_hdd_cfg80211_roam_metrics_handover() - roam metrics hand over
* @adapter: Pointer to adapter
* @roam_info: Pointer to roam info
* @preauth_status: Preauth status
*
* 802.11r/LFR metrics reporting function to report handover initiation
*
* Return: QDF status
*/
QDF_STATUS
wlan_hdd_cfg80211_roam_metrics_preauth_status(struct hdd_adapter *adapter,
struct csr_roam_info *roam_info,
bool preauth_status)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
hdd_enter();
if (!adapter) {
hdd_err("adapter is NULL!");
return QDF_STATUS_E_FAILURE;
}
/* create the event */
memset(&wrqu, 0, sizeof(wrqu));
memset(metrics_notification, 0, sizeof(metrics_notification));
scnprintf(metrics_notification, sizeof(metrics_notification),
"QCOM: LFR_PREAUTH_STATUS " QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(roam_info->bssid.bytes));
if (1 == preauth_status)
strlcat(metrics_notification, " true",
sizeof(metrics_notification));
else
strlcat(metrics_notification, " false",
sizeof(metrics_notification));
wrqu.data.pointer = metrics_notification;
wrqu.data.length = strlen(metrics_notification);
wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu,
metrics_notification);
hdd_exit();
return QDF_STATUS_SUCCESS;
}
/**
* wlan_hdd_cfg80211_roam_metrics_handover() - roam metrics hand over
* @adapter: Pointer to adapter
* @roam_info: Pointer to roam info
*
* 802.11r/LFR metrics reporting function to report handover initiation
*
* Return: QDF status
*/
QDF_STATUS
wlan_hdd_cfg80211_roam_metrics_handover(struct hdd_adapter *adapter,
struct csr_roam_info *roam_info)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
hdd_enter();
if (!adapter) {
hdd_err("adapter is NULL!");
return QDF_STATUS_E_FAILURE;
}
/* create the event */
memset(&wrqu, 0, sizeof(wrqu));
memset(metrics_notification, 0, sizeof(metrics_notification));
wrqu.data.pointer = metrics_notification;
wrqu.data.length = scnprintf(metrics_notification,
sizeof(metrics_notification),
"QCOM: LFR_PREAUTH_HANDOVER "
QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(roam_info->bssid.bytes));
wireless_send_event(adapter->dev, IWEVCUSTOM, &wrqu,
metrics_notification);
hdd_exit();
return QDF_STATUS_SUCCESS;
}
#endif
#ifdef FEATURE_MONITOR_MODE_SUPPORT
static
void hdd_mon_select_cbmode(struct hdd_adapter *adapter,
uint8_t operationChannel,
struct ch_params *ch_params)
{
struct hdd_station_ctx *station_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct hdd_mon_set_ch_info *ch_info = &station_ctx->ch_info;
enum hdd_dot11_mode hdd_dot11_mode;
uint8_t ini_dot11_mode =
(WLAN_HDD_GET_CTX(adapter))->config->dot11Mode;
hdd_debug("Dot11Mode is %u", ini_dot11_mode);
switch (ini_dot11_mode) {
case eHDD_DOT11_MODE_AUTO:
case eHDD_DOT11_MODE_11ax:
case eHDD_DOT11_MODE_11ax_ONLY:
if (sme_is_feature_supported_by_fw(DOT11AX))
hdd_dot11_mode = eHDD_DOT11_MODE_11ax;
else if (sme_is_feature_supported_by_fw(DOT11AC))
hdd_dot11_mode = eHDD_DOT11_MODE_11ac;
else
hdd_dot11_mode = eHDD_DOT11_MODE_11n;
break;
case eHDD_DOT11_MODE_11ac:
case eHDD_DOT11_MODE_11ac_ONLY:
if (sme_is_feature_supported_by_fw(DOT11AC))
hdd_dot11_mode = eHDD_DOT11_MODE_11ac;
else
hdd_dot11_mode = eHDD_DOT11_MODE_11n;
break;
case eHDD_DOT11_MODE_11n:
case eHDD_DOT11_MODE_11n_ONLY:
hdd_dot11_mode = eHDD_DOT11_MODE_11n;
break;
default:
hdd_dot11_mode = ini_dot11_mode;
break;
}
ch_info->channel_width = ch_params->ch_width;
ch_info->phy_mode =
hdd_cfg_xlate_to_csr_phy_mode(hdd_dot11_mode);
ch_info->channel = operationChannel;
ch_info->cb_mode = ch_params->ch_width;
hdd_debug("ch_info width %d, phymode %d channel %d",
ch_info->channel_width, ch_info->phy_mode,
ch_info->channel);
}
#else
static
void hdd_mon_select_cbmode(struct hdd_adapter *adapter,
uint8_t operationChannel,
struct ch_params *ch_params)
{
}
#endif
/**
* hdd_select_cbmode() - select channel bonding mode
* @adapter: Pointer to adapter
* @operatingChannel: Operating channel
* @ch_params: channel info struct to populate
*
* Return: none
*/
void hdd_select_cbmode(struct hdd_adapter *adapter, uint8_t operationChannel,
struct ch_params *ch_params)
{
uint8_t sec_ch = 0;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct hdd_station_ctx *station_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
eConnectionState connstate;
bool cbmode_select = false;
/*
* CDS api expects secondary channel for calculating
* the channel params
*/
if ((ch_params->ch_width == CH_WIDTH_40MHZ) &&
(WLAN_REG_IS_24GHZ_CH(operationChannel))) {
if (operationChannel >= 1 && operationChannel <= 5)
sec_ch = operationChannel + 4;
else if (operationChannel >= 6 && operationChannel <= 13)
sec_ch = operationChannel - 4;
}
/* This call decides required channel bonding mode */
wlan_reg_set_channel_params(hdd_ctx->pdev, operationChannel,
sec_ch, ch_params);
if (adapter->device_mode == QDF_STA_MODE &&
ucfg_mlme_is_change_channel_bandwidth_enabled(hdd_ctx->psoc)) {
connstate = station_ctx->conn_info.conn_state;
if (!(eConnectionState_Associated == connstate ||
eConnectionState_Connecting == connstate)) {
cbmode_select = true;
}
}
if (cds_get_conparam() == QDF_GLOBAL_MONITOR_MODE || cbmode_select)
hdd_mon_select_cbmode(adapter, operationChannel, ch_params);
}
/**
* wlan_hdd_handle_sap_sta_dfs_conc() - to handle SAP STA DFS conc
* @adapter: STA adapter
* @roam_profile: STA roam profile
*
* This routine will move SAP from dfs to non-dfs, if sta is coming up.
*
* Return: false if sta-sap conc is not allowed, else return true
*/
static
bool wlan_hdd_handle_sap_sta_dfs_conc(struct hdd_adapter *adapter,
struct csr_roam_profile *roam_profile)
{
struct hdd_context *hdd_ctx;
struct hdd_adapter *ap_adapter;
struct hdd_ap_ctx *hdd_ap_ctx;
struct hdd_hostapd_state *hostapd_state;
uint8_t channel = 0;
QDF_STATUS status;
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
if (!hdd_ctx) {
hdd_err("HDD context is NULL");
return true;
}
ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
/* probably no sap running, no handling required */
if (!ap_adapter)
return true;
/*
* sap is not in started state, so it is fine to go ahead with sta.
* if sap is currently doing CAC then don't allow sta to go further.
*/
if (!test_bit(SOFTAP_BSS_STARTED, &(ap_adapter)->event_flags) &&
(hdd_ctx->dev_dfs_cac_status != DFS_CAC_IN_PROGRESS))
return true;
if (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS) {
hdd_err("Concurrent SAP is in CAC state, STA is not allowed");
return false;
}
/*
* log and return error, if we allow STA to go through, we don't
* know what is going to happen better stop sta connection
*/
hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
if (!hdd_ap_ctx) {
hdd_err("AP context not found");
return false;
}
/* sap is on non-dfs channel, nothing to handle */
if (!wlan_reg_is_dfs_ch(hdd_ctx->pdev,
hdd_ap_ctx->operating_channel)) {
hdd_info("sap is on non-dfs channel, sta is allowed");
return true;
}
/*
* find out by looking in to scan cache where sta is going to
* connect by passing its roam_profile.
*/
status = policy_mgr_get_channel_from_scan_result(hdd_ctx->psoc,
roam_profile, &channel);
/*
* If the STA's channel is 2.4 GHz, then set pcl with only 2.4 GHz
* channels for roaming case.
*/
if (WLAN_REG_IS_24GHZ_CH(channel)) {
hdd_info("sap is on dfs, new sta conn on 2.4 is allowed");
return true;
}
/*
* If channel is 0 or DFS or LTE unsafe then better to call pcl and
* find out the best channel. If channel is non-dfs 5 GHz then
* better move SAP to STA's channel to make scc, so we have room
* for 3port MCC scenario.
*/
if (!channel || wlan_reg_is_dfs_ch(hdd_ctx->pdev, channel) ||
!policy_mgr_is_safe_channel(hdd_ctx->psoc, channel))
channel = policy_mgr_get_nondfs_preferred_channel(
hdd_ctx->psoc, PM_SAP_MODE, true);
hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter);
qdf_event_reset(&hostapd_state->qdf_event);
status = wlansap_set_channel_change_with_csa(
WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), channel,
hdd_ap_ctx->sap_config.ch_width_orig, false);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Set channel with CSA IE failed, can't allow STA");
return false;
}
/*
* wait here for SAP to finish the channel switch. When channel
* switch happens, SAP sends few beacons with CSA_IE. After
* successfully Transmission of those beacons, it will move its
* state from started to disconnected and move to new channel.
* once it moves to new channel, sap again moves its state
* machine from disconnected to started and set this event.
* wait for 10 secs to finish this.
*/
status = qdf_wait_for_event_completion(&hostapd_state->qdf_event, 10000);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("wait for qdf_event failed, STA not allowed!!");
return false;
}
return true;
}
#ifdef WLAN_FEATURE_11W
/**
* wlan_hdd_cfg80211_check_pmf_valid() - check if pmf status is ok
* @roam_profile: pointer to roam profile
*
* if MFPEnabled is set but the peer AP is non-PMF i.e 80211w=2
* or pmf=2 is an explicit configuration in the supplicant
* configuration, drop the connection request.
*
* Return: 0 if check result is valid, otherwise return error code
*/
static
int wlan_hdd_cfg80211_check_pmf_valid(struct csr_roam_profile *roam_profile)
{
if (roam_profile->MFPEnabled &&
!(roam_profile->MFPRequired || roam_profile->MFPCapable)) {
hdd_err("Drop connect req as supplicant has indicated PMF required for the non-PMF peer. MFPEnabled %d MFPRequired %d MFPCapable %d",
roam_profile->MFPEnabled,
roam_profile->MFPRequired,
roam_profile->MFPCapable);
return -EINVAL;
}
return 0;
}
#else
static inline
int wlan_hdd_cfg80211_check_pmf_valid(struct csr_roam_profile *roam_profile)
{
return 0;
}
#endif
/**
* wlan_hdd_cfg80211_connect_start() - to start the association process
* @adapter: Pointer to adapter
* @ssid: Pointer to ssid
* @ssid_len: Length of ssid
* @bssid: Pointer to bssid
* @bssid_hint: Pointer to bssid hint
* @operatingChannel: Operating channel
* @ch_width: channel width. this is needed only for IBSS
*
* This function is used to start the association process
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_connect_start(struct hdd_adapter *adapter,
const u8 *ssid, size_t ssid_len,
const u8 *bssid, const u8 *bssid_hint,
u8 operatingChannel,
enum nl80211_chan_width ch_width)
{
int status = 0;
QDF_STATUS qdf_status;
struct hdd_context *hdd_ctx;
struct hdd_station_ctx *hdd_sta_ctx;
uint32_t roam_id = INVALID_ROAM_ID;
struct csr_roam_profile *roam_profile;
eCsrAuthType rsn_auth_type;
struct sme_config_params *sme_config;
uint8_t channel = 0;
mac_handle_t mac_handle;
uint8_t wmm_mode = 0;
uint8_t value = 0;
struct wlan_objmgr_vdev *vdev;
uint32_t channel_bonding_mode;
hdd_enter();
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
goto ret_status;
if (WLAN_SSID_MAX_LEN < ssid_len) {
hdd_err("wrong SSID len");
status = -EINVAL;
goto ret_status;
}
if (true == hdd_is_connection_in_progress(NULL, NULL)) {
hdd_err("Connection refused: conn in progress");
status = -EINVAL;
goto ret_status;
}
/* Disable roaming on all other adapters before connect start */
wlan_hdd_disable_roaming(adapter);
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev) {
status = -EINVAL;
goto ret_status;
}
hdd_notify_teardown_tdls_links(vdev);
hdd_objmgr_put_vdev(vdev);
qdf_mem_zero(&hdd_sta_ctx->conn_info.conn_flag,
sizeof(hdd_sta_ctx->conn_info.conn_flag));
roam_profile = hdd_roam_profile(adapter);
if (roam_profile) {
struct hdd_station_ctx *sta_ctx;
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
/* Restart the opportunistic timer
*
* If hw_mode_change_in_progress is true, then wait
* till firmware sends the callback for hw_mode change.
*
* Else set connect_in_progress as true and proceed.
*/
policy_mgr_restart_opportunistic_timer(
hdd_ctx->psoc, false);
if (policy_mgr_is_hw_mode_change_in_progress(
hdd_ctx->psoc)) {
qdf_status = policy_mgr_wait_for_connection_update(
hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("qdf wait for event failed!!");
status = -EINVAL;
goto ret_status;
}
}
hdd_set_connection_in_progress(true);
status = ucfg_mlme_get_wmm_mode(hdd_ctx->psoc, &wmm_mode);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Get wmm_mode failed");
status = -EINVAL;
goto ret_status;
}
if (HDD_WMM_USER_MODE_NO_QOS == wmm_mode) {
/*QoS not enabled in cfg file */
roam_profile->uapsd_mask = 0;
} else {
/*QoS enabled, update uapsd mask from cfg file */
status = ucfg_mlme_get_wmm_uapsd_mask(hdd_ctx->psoc,
&value);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("Get uapsd_mask failed");
status = -EINVAL;
goto ret_status;
}
roam_profile->uapsd_mask = value;
}
roam_profile->SSIDs.numOfSSIDs = 1;
roam_profile->SSIDs.SSIDList->SSID.length = ssid_len;
qdf_mem_zero(roam_profile->SSIDs.SSIDList->SSID.ssId,
sizeof(roam_profile->SSIDs.SSIDList->SSID.ssId));
qdf_mem_copy((void *)(roam_profile->SSIDs.SSIDList->SSID.ssId),
ssid, ssid_len);
/* cleanup bssid hint */
qdf_mem_zero(roam_profile->bssid_hint.bytes,
QDF_MAC_ADDR_SIZE);
qdf_mem_zero((void *)(roam_profile->BSSIDs.bssid),
QDF_MAC_ADDR_SIZE);
if (bssid) {
roam_profile->BSSIDs.numOfBSSIDs = 1;
qdf_mem_copy((void *)(roam_profile->BSSIDs.bssid),
bssid, QDF_MAC_ADDR_SIZE);
/*
* Save BSSID in separate variable as
* roam_profile's BSSID is getting zeroed out in the
* association process. In case of join failure
* we should send valid BSSID to supplicant
*/
qdf_mem_copy(sta_ctx->requested_bssid.bytes,
bssid, QDF_MAC_ADDR_SIZE);
hdd_debug("bssid is given by upper layer %pM", bssid);
} else if (bssid_hint) {
qdf_mem_copy(roam_profile->bssid_hint.bytes,
bssid_hint, QDF_MAC_ADDR_SIZE);
/*
* Save BSSID in a separate variable as
* roam_profile's BSSID is getting zeroed out in the
* association process. In case of join failure
* we should send valid BSSID to supplicant
*/
qdf_mem_copy(sta_ctx->requested_bssid.bytes,
bssid_hint, QDF_MAC_ADDR_SIZE);
hdd_debug("bssid_hint is given by upper layer %pM",
bssid_hint);
}
hdd_debug("Connect to SSID: %.*s operating Channel: %u",
roam_profile->SSIDs.SSIDList->SSID.length,
roam_profile->SSIDs.SSIDList->SSID.ssId,
operatingChannel);
if (hdd_sta_ctx->wpa_versions) {
hdd_set_genie_to_csr(adapter, &rsn_auth_type);
hdd_set_csr_auth_type(adapter, rsn_auth_type);
}
#ifdef FEATURE_WLAN_WAPI
if (adapter->wapi_info.wapi_mode) {
hdd_debug("Setting WAPI AUTH Type and Encryption Mode values");
switch (adapter->wapi_info.wapi_auth_mode) {
case WAPI_AUTH_MODE_PSK:
{
hdd_debug("WAPI AUTH TYPE: PSK: %d",
adapter->wapi_info.wapi_auth_mode);
roam_profile->AuthType.authType[0] =
eCSR_AUTH_TYPE_WAPI_WAI_PSK;
break;
}
case WAPI_AUTH_MODE_CERT:
{
hdd_debug("WAPI AUTH TYPE: CERT: %d",
adapter->wapi_info.wapi_auth_mode);
roam_profile->AuthType.authType[0] =
eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE;
break;
}
default:
break;
} /* End of switch */
if (adapter->wapi_info.wapi_auth_mode ==
WAPI_AUTH_MODE_PSK
|| adapter->wapi_info.wapi_auth_mode ==
WAPI_AUTH_MODE_CERT) {
hdd_debug("WAPI PAIRWISE/GROUP ENCRYPTION: WPI");
roam_profile->AuthType.numEntries = 1;
roam_profile->EncryptionType.numEntries = 1;
roam_profile->EncryptionType.encryptionType[0] =
eCSR_ENCRYPT_TYPE_WPI;
roam_profile->mcEncryptionType.numEntries = 1;
roam_profile->mcEncryptionType.
encryptionType[0] = eCSR_ENCRYPT_TYPE_WPI;
}
}
#endif
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev) {
status = -EINVAL;
goto conn_failure;
}
ucfg_pmo_flush_gtk_offload_req(vdev);
hdd_objmgr_put_vdev(vdev);
roam_profile->csrPersona = adapter->device_mode;
if (operatingChannel) {
roam_profile->ChannelInfo.ChannelList =
&operatingChannel;
roam_profile->ChannelInfo.numOfChannels = 1;
} else {
roam_profile->ChannelInfo.ChannelList = NULL;
roam_profile->ChannelInfo.numOfChannels = 0;
}
if ((QDF_IBSS_MODE == adapter->device_mode)
&& operatingChannel) {
/*
* Need to post the IBSS power save parameters
* to WMA. WMA will configure this parameters
* to firmware if power save is enabled by the
* firmware.
*/
qdf_status = hdd_set_ibss_power_save_params(adapter);
if (QDF_STATUS_SUCCESS != qdf_status) {
hdd_err("Set IBSS Power Save Params Failed");
status = -EINVAL;
goto conn_failure;
}
roam_profile->ch_params.ch_width =
hdd_map_nl_chan_width(ch_width);
/*
* In IBSS mode while operating in 2.4 GHz,
* the device supports only 20 MHz.
*/
if (WLAN_REG_IS_24GHZ_CH(operatingChannel))
roam_profile->ch_params.ch_width =
CH_WIDTH_20MHZ;
hdd_select_cbmode(adapter, operatingChannel,
&roam_profile->ch_params);
}
if (wlan_hdd_cfg80211_check_pmf_valid(roam_profile)) {
status = -EINVAL;
goto conn_failure;
}
/*
* After 8-way handshake supplicant should give the scan command
* in that it update the additional IEs, But because of scan
* enhancements, the supplicant is not issuing the scan command
* now. So the unicast frames which are sent from the host are
* not having the additional IEs. If it is P2P CLIENT and there
* is no additional IE present in roamProfile, then use the
* addtional IE form scan_info
*/
if ((adapter->device_mode == QDF_P2P_CLIENT_MODE) &&
(!roam_profile->pAddIEScan)) {
roam_profile->pAddIEScan =
&adapter->scan_info.scan_add_ie.addIEdata[0];
roam_profile->nAddIEScanLength =
adapter->scan_info.scan_add_ie.length;
}
if ((policy_mgr_is_hw_dbs_capable(hdd_ctx->psoc) == true)
&& (false == wlan_hdd_handle_sap_sta_dfs_conc(adapter,
roam_profile))) {
hdd_err("sap-sta conc will fail, can't allow sta");
hdd_conn_set_connection_state(adapter,
eConnectionState_NotConnected);
status = -ENOMEM;
goto conn_failure;
}
sme_config = qdf_mem_malloc(sizeof(*sme_config));
if (!sme_config) {
hdd_conn_set_connection_state(adapter,
eConnectionState_NotConnected);
status = -ENOMEM;
goto conn_failure;
}
mac_handle = hdd_ctx->mac_handle;
sme_get_config_param(mac_handle, sme_config);
/* These values are not sessionized. So, any change in these SME
* configs on an older or parallel interface will affect the
* cb mode. So, restoring the default INI params before starting
* interfaces such as sta, cli etc.,
*/
ucfg_mlme_get_channel_bonding_5ghz(hdd_ctx->psoc,
&channel_bonding_mode);
sme_config->csr_config.channelBondingMode5GHz =
channel_bonding_mode;
ucfg_mlme_get_channel_bonding_24ghz(hdd_ctx->psoc,
&channel_bonding_mode);
sme_config->csr_config.channelBondingMode24GHz =
channel_bonding_mode;
sme_update_config(mac_handle, sme_config);
qdf_mem_free(sme_config);
/*
* Change conn_state to connecting before sme_roam_connect(),
* because sme_roam_connect() has a direct path to call
* hdd_sme_roam_callback(), which will change the conn_state
* If direct path, conn_state will be accordingly changed to
* NotConnected or Associated by either
* hdd_association_completion_handler() or
* hdd_dis_connect_handler() in sme_RoamCallback()if
* sme_RomConnect is to be queued,
* Connecting state will remain until it is completed.
*
* If connection state is not changed, connection state will
* remain in eConnectionState_NotConnected state.
* In hdd_association_completion_handler, "hddDisconInProgress"
* is set to true if conn state is
* eConnectionState_NotConnected.
* If "hddDisconInProgress" is set to true then cfg80211 layer
* is not informed of connect result indication which
* is an issue.
*/
if (QDF_STA_MODE == adapter->device_mode ||
QDF_P2P_CLIENT_MODE == adapter->device_mode)
hdd_conn_set_connection_state(adapter,
eConnectionState_Connecting);
hdd_set_disconnect_status(adapter, false);
qdf_runtime_pm_prevent_suspend(
&hdd_ctx->runtime_context.connect);
hdd_prevent_suspend_timeout(HDD_WAKELOCK_CONNECT_COMPLETE,
WIFI_POWER_EVENT_WAKELOCK_CONNECT);
qdf_status = sme_roam_connect(mac_handle,
adapter->vdev_id, roam_profile,
&roam_id);
if (QDF_IS_STATUS_ERROR(qdf_status))
status = qdf_status_to_os_return(qdf_status);
if ((QDF_STATUS_SUCCESS != qdf_status) &&
(QDF_STA_MODE == adapter->device_mode ||
QDF_P2P_CLIENT_MODE == adapter->device_mode)) {
hdd_err("sme_roam_connect (session %d) failed with "
"qdf_status %d. -> NotConnected",
adapter->vdev_id, qdf_status);
/* change back to NotAssociated */
hdd_conn_set_connection_state(adapter,
eConnectionState_NotConnected);
qdf_runtime_pm_allow_suspend(
&hdd_ctx->runtime_context.connect);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_CONNECT);
}
/* Reset connect_in_progress */
hdd_set_connection_in_progress(false);
roam_profile->ChannelInfo.ChannelList = NULL;
roam_profile->ChannelInfo.numOfChannels = 0;
if ((QDF_STA_MODE == adapter->device_mode)
&& policy_mgr_is_current_hwmode_dbs(hdd_ctx->psoc)
&& !policy_mgr_is_hw_dbs_2x2_capable(
hdd_ctx->psoc)) {
policy_mgr_get_channel_from_scan_result(
hdd_ctx->psoc,
roam_profile, &channel);
hdd_info("Move to single MAC mode(optimization) if applicable");
if (channel)
policy_mgr_checkn_update_hw_mode_single_mac_mode(
hdd_ctx->psoc, channel);
}
} else {
hdd_err("No valid Roam profile");
status = -EINVAL;
}
goto ret_status;
conn_failure:
/* Reset connect_in_progress */
hdd_set_connection_in_progress(false);
ret_status:
/*
* Enable roaming on other STA adapter for failure case.
* For success case, it is enabled in assoc completion handler
*/
if (status)
wlan_hdd_enable_roaming(adapter);
hdd_exit();
return status;
}
/**
* wlan_hdd_cfg80211_set_auth_type() - set auth type
* @adapter: Pointer to adapter
* @auth_type: Auth type
*
* This function is used to set the authentication type (OPEN/SHARED).
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_auth_type(struct hdd_adapter *adapter,
enum nl80211_auth_type auth_type)
{
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct csr_roam_profile *roam_profile;
/*set authentication type */
switch (auth_type) {
case NL80211_AUTHTYPE_AUTOMATIC:
hdd_debug("set authentication type to AUTOSWITCH");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_AUTOSWITCH;
break;
case NL80211_AUTHTYPE_OPEN_SYSTEM:
case NL80211_AUTHTYPE_FT:
hdd_debug("set authentication type to OPEN");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
hdd_debug("set authentication type to SHARED");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_SHARED_KEY;
break;
#ifdef FEATURE_WLAN_ESE
case NL80211_AUTHTYPE_NETWORK_EAP:
hdd_debug("set authentication type to CCKM WPA");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_CCKM_WPA;
break;
#endif
#if defined(WLAN_FEATURE_FILS_SK) && \
(defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)))
case NL80211_AUTHTYPE_FILS_SK:
hdd_debug("set authentication type to FILS SHARED");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM;
break;
#endif
case NL80211_AUTHTYPE_SAE:
hdd_debug("set authentication type to SAE");
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_SAE;
break;
default:
hdd_err("Unsupported authentication type: %d", auth_type);
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_UNKNOWN;
return -EINVAL;
}
roam_profile = hdd_roam_profile(adapter);
roam_profile->AuthType.authType[0] = sta_ctx->conn_info.auth_type;
return 0;
}
#if defined(WLAN_FEATURE_FILS_SK) && \
(defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)))
static bool hdd_validate_fils_info_ptr(struct csr_roam_profile *roam_profile)
{
struct cds_fils_connection_info *fils_con_info;
fils_con_info = roam_profile->fils_con_info;
if (!fils_con_info) {
hdd_err("No valid Roam profile");
return false;
}
return true;
}
static enum eAniAuthType wlan_hdd_get_fils_auth_type(
enum nl80211_auth_type auth)
{
switch (auth) {
case NL80211_AUTHTYPE_FILS_SK:
return SIR_FILS_SK_WITHOUT_PFS;
case NL80211_AUTHTYPE_FILS_SK_PFS:
return SIR_FILS_SK_WITH_PFS;
case NL80211_AUTHTYPE_FILS_PK:
return SIR_FILS_PK_AUTH;
default:
return eSIR_DONOT_USE_AUTH_TYPE;
}
}
static bool wlan_hdd_fils_data_in_limits(struct cfg80211_connect_params *req)
{
hdd_debug("seq=%d auth=%d lengths: user=%zu rrk=%zu realm=%zu",
req->fils_erp_next_seq_num, req->auth_type,
req->fils_erp_username_len, req->fils_erp_rrk_len,
req->fils_erp_realm_len);
if (!req->fils_erp_rrk_len || !req->fils_erp_realm_len ||
!req->fils_erp_username_len ||
req->fils_erp_rrk_len > FILS_MAX_RRK_LENGTH ||
req->fils_erp_realm_len > FILS_MAX_REALM_LEN ||
req->fils_erp_username_len > FILS_MAX_KEYNAME_NAI_LENGTH) {
hdd_err("length incorrect, user=%zu rrk=%zu realm=%zu",
req->fils_erp_username_len, req->fils_erp_rrk_len,
req->fils_erp_realm_len);
return false;
}
if (!req->fils_erp_rrk || !req->fils_erp_realm ||
!req->fils_erp_username) {
hdd_err("buffer incorrect, user=%pK rrk=%pK realm=%pK",
req->fils_erp_username, req->fils_erp_rrk,
req->fils_erp_realm);
return false;
}
return true;
}
/**
* wlan_hdd_cfg80211_set_fils_config() - set fils config params during connect
* @adapter: Pointer to adapter
* @req: Pointer to fils parameters
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_fils_config(struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
struct csr_roam_profile *roam_profile;
enum eAniAuthType auth_type;
uint8_t *buf;
bool value;
QDF_STATUS status;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
roam_profile = hdd_roam_profile(adapter);
value = 0;
status = ucfg_mlme_get_fils_enabled_info(hdd_ctx->psoc, &value);
if (QDF_IS_STATUS_ERROR(status) || !value) {
hdd_err("get_fils_enabled status: %d fils_enabled: %d",
status, value);
return -EINVAL;
}
hdd_clear_fils_connection_info(adapter);
roam_profile->fils_con_info =
qdf_mem_malloc(sizeof(*roam_profile->fils_con_info));
if (!roam_profile->fils_con_info)
return -EINVAL;
/*
* The initial connection for FILS may happen with an OPEN
* auth type. Hence we need to allow the connection to go
* through in that case as well. Below is_fils_connection
* flag is propagated down to CSR and PE sessions through
* the JOIN request. As the flag is used, do not free the
* memory allocated to fils_con_info and return success.
*/
if (req->auth_type != NL80211_AUTHTYPE_FILS_SK) {
roam_profile->fils_con_info->is_fils_connection = false;
return 0;
}
/*
* Once above check is done, then we can check for valid FILS
* auth types. Currently only NL80211_AUTHTYPE_FILS_SK is
* supported. Once all auth types are supported, then we can
* merge these 2 conditions into one.
*/
auth_type = wlan_hdd_get_fils_auth_type(req->auth_type);
if (auth_type == eSIR_DONOT_USE_AUTH_TYPE) {
hdd_err("invalid auth type for fils %d", req->auth_type);
goto fils_conn_fail;
}
if (!wlan_hdd_fils_data_in_limits(req))
goto fils_conn_fail;
roam_profile->fils_con_info->is_fils_connection = true;
roam_profile->fils_con_info->sequence_number =
(req->fils_erp_next_seq_num + 1);
roam_profile->fils_con_info->auth_type = auth_type;
roam_profile->fils_con_info->r_rk_length =
req->fils_erp_rrk_len;
if (req->fils_erp_rrk_len)
qdf_mem_copy(roam_profile->fils_con_info->r_rk,
req->fils_erp_rrk,
roam_profile->fils_con_info->r_rk_length);
roam_profile->fils_con_info->realm_len = req->fils_erp_realm_len;
if (req->fils_erp_realm_len)
qdf_mem_copy(roam_profile->fils_con_info->realm,
req->fils_erp_realm,
roam_profile->fils_con_info->realm_len);
roam_profile->fils_con_info->key_nai_length =
req->fils_erp_username_len + sizeof(char) +
req->fils_erp_realm_len;
hdd_debug("key_nai_length = %d",
roam_profile->fils_con_info->key_nai_length);
if (roam_profile->fils_con_info->key_nai_length >
FILS_MAX_KEYNAME_NAI_LENGTH) {
hdd_err("Do not allow FILS conn due to excess NAI Length %d",
roam_profile->fils_con_info->key_nai_length);
goto fils_conn_fail;
}
buf = roam_profile->fils_con_info->keyname_nai;
qdf_mem_copy(buf, req->fils_erp_username, req->fils_erp_username_len);
buf += req->fils_erp_username_len;
*buf++ = '@';
qdf_mem_copy(buf, req->fils_erp_realm, req->fils_erp_realm_len);
return 0;
fils_conn_fail:
if (roam_profile->fils_con_info) {
qdf_mem_free(roam_profile->fils_con_info);
roam_profile->fils_con_info = NULL;
}
return -EINVAL;
}
static bool wlan_hdd_is_akm_suite_fils(uint32_t key_mgmt)
{
switch (key_mgmt) {
case WLAN_AKM_SUITE_FILS_SHA256:
case WLAN_AKM_SUITE_FILS_SHA384:
case WLAN_AKM_SUITE_FT_FILS_SHA256:
case WLAN_AKM_SUITE_FT_FILS_SHA384:
return true;
default:
return false;
}
}
static bool wlan_hdd_is_conn_type_fils(struct cfg80211_connect_params *req)
{
enum nl80211_auth_type auth_type = req->auth_type;
/*
* Below n_akm_suites is defined as int in the kernel, even though it
* is supposed to be unsigned.
*/
int num_akm_suites = req->crypto.n_akm_suites;
uint32_t key_mgmt = req->crypto.akm_suites[0];
enum eAniAuthType fils_auth_type =
wlan_hdd_get_fils_auth_type(req->auth_type);
hdd_debug("Num of AKM suites = %d", num_akm_suites);
if (num_akm_suites <= 0)
return false;
/*
* Auth type will be either be OPEN or FILS type for a FILS connection
*/
if ((auth_type != NL80211_AUTHTYPE_OPEN_SYSTEM) &&
(fils_auth_type == eSIR_DONOT_USE_AUTH_TYPE)) {
hdd_debug("Not a FILS auth type, auth = %d, fils auth = %d",
auth_type, fils_auth_type);
return false;
}
if (!wlan_hdd_is_akm_suite_fils(key_mgmt)) {
hdd_debug("Not a FILS AKM SUITE %d", key_mgmt);
return false;
}
return true;
}
#else
static bool hdd_validate_fils_info_ptr(struct csr_roam_profile *roam_profile)
{
return true;
}
static int wlan_hdd_cfg80211_set_fils_config(struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
return 0;
}
static bool wlan_hdd_is_akm_suite_fils(uint32_t key_mgmt)
{
return false;
}
static bool wlan_hdd_is_conn_type_fils(struct cfg80211_connect_params *req)
{
return false;
}
#endif
/**
* wlan_hdd_set_akm_suite() - set key management type
* @adapter: Pointer to adapter
* @key_mgmt: Key management type
*
* This function is used to set the key mgmt type(PSK/8021x).
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_set_akm_suite(struct hdd_adapter *adapter, u32 key_mgmt)
{
struct hdd_station_ctx *sta_ctx;
struct csr_roam_profile *roam_profile;
roam_profile = hdd_roam_profile(adapter);
if (wlan_hdd_is_akm_suite_fils(key_mgmt) &&
!hdd_validate_fils_info_ptr(roam_profile))
return -EINVAL;
#ifndef WLAN_AKM_SUITE_8021X_SHA256
#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05
#endif
#ifndef WLAN_AKM_SUITE_PSK_SHA256
#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06
#endif
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
/*set key mgmt type */
switch (key_mgmt) {
case WLAN_AKM_SUITE_PSK:
case WLAN_AKM_SUITE_PSK_SHA256:
case WLAN_AKM_SUITE_FT_PSK:
case WLAN_AKM_SUITE_DPP_RSN:
hdd_debug("setting key mgmt type to PSK");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_PSK;
break;
case WLAN_AKM_SUITE_8021X_SHA256:
case WLAN_AKM_SUITE_8021X:
case WLAN_AKM_SUITE_FT_8021X:
hdd_debug("setting key mgmt type to 8021x");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
#ifdef FEATURE_WLAN_ESE
#define WLAN_AKM_SUITE_CCKM 0x00409600 /* Should be in ieee802_11_defs.h */
case WLAN_AKM_SUITE_CCKM:
hdd_debug("setting key mgmt type to CCKM");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_CCKM;
break;
#endif
#ifndef WLAN_AKM_SUITE_OSEN
#define WLAN_AKM_SUITE_OSEN 0x506f9a01 /* Should be in ieee802_11_defs.h */
#endif
case WLAN_AKM_SUITE_OSEN:
hdd_debug("setting key mgmt type to OSEN");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
#if defined(WLAN_FEATURE_FILS_SK) && \
(defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)))
case WLAN_AKM_SUITE_FILS_SHA256:
hdd_debug("setting key mgmt type to FILS SHA256");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
roam_profile->fils_con_info->akm_type =
eCSR_AUTH_TYPE_FILS_SHA256;
break;
case WLAN_AKM_SUITE_FILS_SHA384:
hdd_debug("setting key mgmt type to FILS SHA384");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
roam_profile->fils_con_info->akm_type =
eCSR_AUTH_TYPE_FILS_SHA384;
break;
case WLAN_AKM_SUITE_FT_FILS_SHA256:
hdd_debug("setting key mgmt type to FILS FT SHA256");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
roam_profile->fils_con_info->akm_type =
eCSR_AUTH_TYPE_FT_FILS_SHA256;
break;
case WLAN_AKM_SUITE_FT_FILS_SHA384:
hdd_debug("setting key mgmt type to FILS FT SHA384");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
roam_profile->fils_con_info->akm_type =
eCSR_AUTH_TYPE_FT_FILS_SHA384;
break;
#endif
case WLAN_AKM_SUITE_OWE:
hdd_debug("setting key mgmt type to OWE");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
case WLAN_AKM_SUITE_EAP_SHA256:
hdd_debug("setting key mgmt type to EAP_SHA256");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
case WLAN_AKM_SUITE_EAP_SHA384:
hdd_debug("setting key mgmt type to EAP_SHA384");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
case WLAN_AKM_SUITE_SAE:
hdd_debug("setting key mgmt type to SAE");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
case WLAN_AKM_SUITE_FT_SAE:
hdd_debug("setting key mgmt type to FT-SAE");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
case WLAN_AKM_SUITE_FT_EAP_SHA_384:
hdd_debug("setting key mgmt type to FT-EAP-SHA384");
sta_ctx->auth_key_mgmt |= HDD_AUTH_KEY_MGMT_802_1X;
break;
default:
hdd_err("Unsupported key mgmt type: %d", key_mgmt);
return -EINVAL;
}
return 0;
}
/**
* wlan_hdd_cfg80211_set_cipher() - set encryption type
* @adapter: Pointer to adapter
* @cipher: Cipher type
* @ucast: Unicast flag
*
* This function is used to set the encryption type
* (NONE/WEP40/WEP104/TKIP/CCMP).
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_cipher(struct hdd_adapter *adapter,
u32 cipher, bool ucast)
{
eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE;
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct csr_roam_profile *roam_profile;
if (!cipher) {
hdd_debug("received cipher %d - considering none", cipher);
encryptionType = eCSR_ENCRYPT_TYPE_NONE;
} else {
/*set encryption method */
switch (cipher) {
case IW_AUTH_CIPHER_NONE:
encryptionType = eCSR_ENCRYPT_TYPE_NONE;
break;
case WLAN_CIPHER_SUITE_WEP40:
encryptionType = eCSR_ENCRYPT_TYPE_WEP40;
break;
case WLAN_CIPHER_SUITE_WEP104:
encryptionType = eCSR_ENCRYPT_TYPE_WEP104;
break;
case WLAN_CIPHER_SUITE_TKIP:
encryptionType = eCSR_ENCRYPT_TYPE_TKIP;
break;
case WLAN_CIPHER_SUITE_CCMP:
encryptionType = eCSR_ENCRYPT_TYPE_AES;
break;
#ifdef FEATURE_WLAN_WAPI
case WLAN_CIPHER_SUITE_SMS4:
encryptionType = eCSR_ENCRYPT_TYPE_WPI;
break;
#endif
#ifdef FEATURE_WLAN_ESE
case WLAN_CIPHER_SUITE_KRK:
encryptionType = eCSR_ENCRYPT_TYPE_KRK;
break;
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
case WLAN_CIPHER_SUITE_BTK:
encryptionType = eCSR_ENCRYPT_TYPE_BTK;
break;
#endif
#endif
case WLAN_CIPHER_SUITE_GCMP:
encryptionType = eCSR_ENCRYPT_TYPE_AES_GCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
encryptionType = eCSR_ENCRYPT_TYPE_AES_GCMP_256;
break;
default:
hdd_err("Unsupported cipher type: %d", cipher);
return -EOPNOTSUPP;
}
}
roam_profile = hdd_roam_profile(adapter);
if (ucast) {
hdd_debug("setting unicast cipher type to %d", encryptionType);
sta_ctx->conn_info.uc_encrypt_type = encryptionType;
roam_profile->EncryptionType.numEntries = 1;
roam_profile->EncryptionType.encryptionType[0] =
encryptionType;
} else {
hdd_debug("setting mcast cipher type to %d", encryptionType);
sta_ctx->conn_info.mc_encrypt_type = encryptionType;
roam_profile->mcEncryptionType.numEntries = 1;
roam_profile->mcEncryptionType.encryptionType[0] =
encryptionType;
}
return 0;
}
/**
* wlan_hdd_add_assoc_ie() - Add Assoc IE to roamProfile
* @adapter: Pointer to adapter
* @gen_ie: Pointer to IE data
* @len: length of IE data
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_add_assoc_ie(struct hdd_adapter *adapter,
const uint8_t *gen_ie, uint16_t len)
{
struct csr_roam_profile *roam_profile;
tSirAddie *assoc_add_ie;
uint16_t cur_add_ie_len;
assoc_add_ie = hdd_assoc_additional_ie(adapter);
cur_add_ie_len = assoc_add_ie->length;
if (SIR_MAC_MAX_ADD_IE_LENGTH < (cur_add_ie_len + len)) {
hdd_err("current len %u, new ie of len %u will overflow",
cur_add_ie_len, len);
return -ENOMEM;
}
memcpy(assoc_add_ie->addIEdata + cur_add_ie_len, gen_ie, len);
assoc_add_ie->length += len;
roam_profile = hdd_roam_profile(adapter);
roam_profile->pAddIEAssoc = assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength = assoc_add_ie->length;
return 0;
}
#ifdef WLAN_FEATURE_FILS_SK
/**
* wlan_hdd_save_hlp_ie - API to save HLP IE
* @roam_profile: Pointer to roam profile
* @gen_ie: IE buffer to store
* @len: length of the IE buffer @gen_ie
* @flush: Flush the older saved HLP if any
*
* Return: None
*/
static void wlan_hdd_save_hlp_ie(struct csr_roam_profile *roam_profile,
const uint8_t *gen_ie, uint16_t len,
bool flush)
{
uint8_t *hlp_ie = roam_profile->hlp_ie;
if (flush) {
roam_profile->hlp_ie_len = 0;
if (hlp_ie) {
qdf_mem_free(hlp_ie);
roam_profile->hlp_ie = NULL;
}
}
if ((roam_profile->hlp_ie_len +
len) > FILS_MAX_HLP_DATA_LEN) {
hdd_err("HLP len exceeds: hlp_ie_len %d len %d",
roam_profile->hlp_ie_len, len);
return;
}
if (!roam_profile->hlp_ie) {
roam_profile->hlp_ie =
qdf_mem_malloc(FILS_MAX_HLP_DATA_LEN);
hlp_ie = roam_profile->hlp_ie;
if (!hlp_ie)
return;
}
qdf_mem_copy(hlp_ie + roam_profile->hlp_ie_len, gen_ie, len);
roam_profile->hlp_ie_len += len;
}
#else
static inline void wlan_hdd_save_hlp_ie(struct csr_roam_profile *roam_profile,
const uint8_t *gen_ie, uint16_t len,
bool flush)
{}
#endif
#ifdef WLAN_CONV_CRYPTO_SUPPORTED
/**
* hdd_populate_crypto_auth_type() - populate auth type for crypto
* @vdev: pointed to vdev obmgr
* @auth_type: legacy auth_type
*
* set the crypto auth type for corresponding auth type received
* from NL
*
* Return: None
*/
static void hdd_populate_crypto_auth_type(struct wlan_objmgr_vdev *vdev,
enum nl80211_auth_type auth_type)
{
QDF_STATUS status;
uint32_t set_val = 0;
wlan_crypto_auth_mode crypto_auth_type =
osif_nl_to_crypto_auth_type(auth_type);
hdd_debug("set auth type %d to crypto component", crypto_auth_type);
HDD_SET_BIT(set_val, crypto_auth_type);
status = wlan_crypto_set_vdev_param(vdev,
WLAN_CRYPTO_PARAM_AUTH_MODE,
set_val);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to set auth type %0X to crypto component",
set_val);
}
/**
* hdd_populate_crypto_akm_type() - populate akm type for crypto
* @vdev: pointed to vdev obmgr
* @akm_type: legacy akm_type
*
* set the crypto akm type for corresponding akm type received
* from NL
*
* Return: None
*/
static void hdd_populate_crypto_akm_type(struct wlan_objmgr_vdev *vdev,
u32 key_mgmt)
{
QDF_STATUS status;
uint32_t set_val = 0;
wlan_crypto_key_mgmt crypto_akm_type =
osif_nl_to_crypto_akm_type(key_mgmt);
hdd_debug("set akm type %d to crypto component", crypto_akm_type);
HDD_SET_BIT(set_val, crypto_akm_type);
status = wlan_crypto_set_vdev_param(vdev,
WLAN_CRYPTO_PARAM_KEY_MGMT,
set_val);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to set akm type %0x to crypto component",
set_val);
}
/**
* hdd_populate_crypto_cipher_type() - populate cipher type for crypto
* @cipher: legacy cipher type
* @vdev: pointed to vdev obmgr
* @cipher_param_type: param type, UCST/MCAST
*
* set the crypto cipher type for corresponding cipher type received
* from NL
*
* Return: None
*/
static void hdd_populate_crypto_cipher_type(u32 cipher,
struct wlan_objmgr_vdev *vdev,
wlan_crypto_param_type
cipher_param_type)
{
QDF_STATUS status;
uint32_t set_val = 0;
wlan_crypto_cipher_type crypto_cipher_type =
osif_nl_to_crypto_cipher_type(cipher);
hdd_debug("set cipher params %d type %d to crypto",
cipher_param_type, crypto_cipher_type);
HDD_SET_BIT(set_val, crypto_cipher_type);
status = wlan_crypto_set_vdev_param(vdev, cipher_param_type, set_val);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Failed to set cipher params %d type %0x to crypto",
cipher_param_type, set_val);
}
/**
* hdd_populate_crypto_params() - set crypto params
* @vdev: Pointer to vdev obh mgr
* @req: Pointer to security parameters
*
* Set Auth, Akm and Cipher type for crypto
*
* Return: None
*/
static void hdd_populate_crypto_params(struct wlan_objmgr_vdev *vdev,
struct cfg80211_connect_params *req)
{
hdd_populate_crypto_auth_type(vdev, req->auth_type);
if (req->crypto.n_akm_suites)
hdd_populate_crypto_akm_type(vdev, req->crypto.akm_suites[0]);
if (req->crypto.n_ciphers_pairwise) {
hdd_populate_crypto_cipher_type(req->crypto.ciphers_pairwise[0],
vdev,
WLAN_CRYPTO_PARAM_UCAST_CIPHER);
} else {
/* Reset previous cipher suite to none */
hdd_populate_crypto_cipher_type(0, vdev,
WLAN_CRYPTO_PARAM_UCAST_CIPHER);
}
hdd_populate_crypto_cipher_type(req->crypto.cipher_group,
vdev,
WLAN_CRYPTO_PARAM_MCAST_CIPHER);
}
/**
* hdd_set_crypto_key_mgmt_param() - Set key mgmt param.
* @adapter: Pointer to adapter.
*
* Return: None
*/
static void hdd_set_crypto_key_mgmt_param(struct hdd_adapter *adapter)
{
uint32_t key_mgmt = 0;
struct wlan_objmgr_vdev *vdev;
if (!adapter) {
hdd_err("adapter is null");
return;
}
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return;
if (adapter->wapi_info.wapi_auth_mode == WAPI_AUTH_MODE_PSK)
HDD_SET_BIT(key_mgmt, WLAN_CRYPTO_KEY_MGMT_WAPI_PSK);
if (adapter->wapi_info.wapi_auth_mode == WAPI_AUTH_MODE_CERT)
HDD_SET_BIT(key_mgmt, WLAN_CRYPTO_KEY_MGMT_WAPI_CERT);
wlan_crypto_set_vdev_param(vdev, WLAN_CRYPTO_PARAM_KEY_MGMT, key_mgmt);
hdd_objmgr_put_vdev(vdev);
}
#else
static inline
void hdd_populate_crypto_auth_type(struct wlan_objmgr_vdev *vdev,
enum nl80211_auth_type auth_type)
{
}
static inline
void hdd_populate_crypto_akm_type(struct wlan_objmgr_vdev *vdev,
u32 key_mgmt)
{
}
static inline
void hdd_populate_crypto_cipher_type(u32 cipher,
struct wlan_objmgr_vdev *vdev,
wlan_crypto_param_type
cipher_param_type)
{
}
static inline
void hdd_populate_crypto_params(struct wlan_objmgr_vdev *vdev,
struct cfg80211_connect_params *req)
{
}
static inline void hdd_set_crypto_key_mgmt_param(struct hdd_adapter *adapter)
{
}
#endif
/**
* wlan_hdd_cfg80211_set_ie() - set IEs
* @adapter: Pointer to adapter
* @ie: Pointer ot ie
* @ie: IE length
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_ie(struct hdd_adapter *adapter,
const uint8_t *ie,
size_t ie_len)
{
struct csr_roam_profile *roam_profile;
tSirAddie *assoc_add_ie;
const uint8_t *genie = ie;
uint16_t remLen = ie_len;
#ifdef FEATURE_WLAN_WAPI
uint32_t akmsuite[MAX_NUM_AKM_SUITES];
uint8_t *tmp;
uint16_t akmsuiteCount;
uint32_t *akmlist;
#endif
int status;
uint8_t *security_ie;
roam_profile = hdd_roam_profile(adapter);
/* clear previous assocAddIE */
assoc_add_ie = hdd_assoc_additional_ie(adapter);
assoc_add_ie->length = 0;
roam_profile->bWPSAssociation = false;
roam_profile->bOSENAssociation = false;
security_ie = hdd_security_ie(adapter);
while (remLen >= 2) {
uint16_t eLen = 0;
uint8_t elementId;
elementId = *genie++;
eLen = *genie++;
remLen -= 2;
/* Sanity check on eLen */
if (eLen > remLen) {
hdd_err("%s: Invalid IE length[%d] for IE[0x%X]",
__func__, eLen, elementId);
QDF_ASSERT(0);
return -EINVAL;
}
hdd_debug("IE[0x%X], LEN[%d]", elementId, eLen);
switch (elementId) {
case DOT11F_EID_WPA:
if (4 > eLen) { /* should have at least OUI which is 4 bytes so extra 2 bytes not needed */
hdd_err("Invalid WPA IE");
return -EINVAL;
} else if (0 ==
memcmp(&genie[0], "\x00\x50\xf2\x04", 4)) {
uint16_t curAddIELen = assoc_add_ie->length;
hdd_debug("Set WPS IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE. Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
/* WSC IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE */
memcpy(assoc_add_ie->addIEdata +
curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->bWPSAssociation = true;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
} else if (0 == memcmp(&genie[0], "\x00\x50\xf2", 3)) {
if (eLen > (WLAN_MAX_IE_LEN - 2)) {
hdd_err("%s: Invalid WPA IE length[%d]",
__func__, eLen);
QDF_ASSERT(0);
return -EINVAL;
}
hdd_debug("Set WPA IE (len %d)", eLen + 2);
memset(security_ie, 0, WLAN_MAX_IE_LEN);
memcpy(security_ie, genie - 2, (eLen + 2));
roam_profile->pWPAReqIE = security_ie;
roam_profile->nWPAReqIELength = eLen + 2; /* ie_len; */
} else if ((0 == memcmp(&genie[0], P2P_OUI_TYPE,
P2P_OUI_TYPE_SIZE))) {
uint16_t curAddIELen =
assoc_add_ie->length;
hdd_debug("Set P2P IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
/* P2P IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE */
memcpy(assoc_add_ie->addIEdata +
curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
}
#ifdef WLAN_FEATURE_WFD
else if ((0 == memcmp(&genie[0], WFD_OUI_TYPE,
WFD_OUI_TYPE_SIZE)) &&
/* Consider WFD IE, only for P2P Client */
(QDF_P2P_CLIENT_MODE ==
adapter->device_mode)) {
uint16_t curAddIELen =
assoc_add_ie->length;
hdd_debug("Set WFD IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
/* WFD IE is saved to Additional IE ; it should
* be accumulated to handle WPS IE + P2P IE +
* WFD IE
*/
memcpy(assoc_add_ie->addIEdata +
curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
}
#endif
/* Appending HS 2.0 Indication Element in Assiciation Request */
else if ((0 == memcmp(&genie[0], HS20_OUI_TYPE,
HS20_OUI_TYPE_SIZE))) {
uint16_t curAddIELen =
assoc_add_ie->length;
hdd_debug("Set HS20 IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
memcpy(assoc_add_ie->addIEdata +
curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
}
/* Appending OSEN Information Element in Assiciation Request */
else if ((0 == memcmp(&genie[0], OSEN_OUI_TYPE,
OSEN_OUI_TYPE_SIZE))) {
uint16_t curAddIELen =
assoc_add_ie->length;
hdd_debug("Set OSEN IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
memcpy(assoc_add_ie->addIEdata +
curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->bOSENAssociation = true;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
} else if ((0 == memcmp(&genie[0], MBO_OUI_TYPE,
MBO_OUI_TYPE_SIZE))){
hdd_debug("Set MBO IE(len %d)", eLen + 2);
status = wlan_hdd_add_assoc_ie(adapter,
genie - 2, eLen + 2);
if (status)
return status;
} else {
uint16_t add_ie_len =
assoc_add_ie->length;
hdd_debug("Set OSEN IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
memcpy(assoc_add_ie->addIEdata +
add_ie_len, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->pAddIEAssoc =
assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength =
assoc_add_ie->length;
}
break;
case DOT11F_EID_RSN:
if (eLen > DOT11F_IE_RSN_MAX_LEN) {
hdd_err("%s: Invalid WPA RSN IE length[%d]",
__func__, eLen);
return -EINVAL;
}
memset(security_ie, 0, WLAN_MAX_IE_LEN);
memcpy(security_ie, genie - 2, (eLen + 2));
roam_profile->pRSNReqIE = security_ie;
roam_profile->nRSNReqIELength = eLen + 2; /* ie_len; */
hdd_debug("Set RSN IE(len %d)", eLen + 2);
break;
/*
* Appending Extended Capabilities with Interworking bit set
* in Assoc Req.
*
* In assoc req this EXT Cap will only be taken into account if
* interworkingService bit is set to 1. Currently
* driver is only interested in interworkingService capability
* from supplicant. If in future any other EXT Cap info is
* required from supplicat, it needs to be handled while
* sending Assoc Req in LIM.
*/
case DOT11F_EID_EXTCAP:
{
uint16_t curAddIELen =
assoc_add_ie->length;
hdd_debug("Set Extended CAPS IE(len %d)", eLen + 2);
if (SIR_MAC_MAX_ADD_IE_LENGTH <
(assoc_add_ie->length + eLen)) {
hdd_err("Cannot accommodate assocAddIE Need bigger buffer space");
QDF_ASSERT(0);
return -ENOMEM;
}
memcpy(assoc_add_ie->addIEdata + curAddIELen, genie - 2, eLen + 2);
assoc_add_ie->length += eLen + 2;
roam_profile->pAddIEAssoc = assoc_add_ie->addIEdata;
roam_profile->nAddIEAssocLength = assoc_add_ie->length;
break;
}
#ifdef FEATURE_WLAN_WAPI
case WLAN_ELEMID_WAPI:
/* Setting WAPI Mode to ON=1 */
adapter->wapi_info.wapi_mode = 1;
hdd_debug("WAPI MODE IS %u", adapter->wapi_info.wapi_mode);
/* genie is pointing to data field of WAPI IE's buffer */
tmp = (uint8_t *)genie;
/* Validate length for Version(2 bytes) and Number
* of AKM suite (2 bytes) in WAPI IE buffer, coming from
* supplicant*/
if (eLen < 4) {
hdd_err("Invalid IE Len: %u", eLen);
return -EINVAL;
}
tmp = tmp + 2; /* Skip Version */
/* Get the number of AKM suite */
akmsuiteCount = WPA_GET_LE16(tmp);
/* Skip the number of AKM suite */
tmp = tmp + 2;
/* Validate total length for WAPI IE's buffer */
if (eLen < (4 + (akmsuiteCount * sizeof(uint32_t)))) {
hdd_err("Invalid IE Len: %u", eLen);
return -EINVAL;
}
/* AKM suite list, each OUI contains 4 bytes */
akmlist = (uint32_t *)(tmp);
if (akmsuiteCount <= MAX_NUM_AKM_SUITES) {
qdf_mem_copy(akmsuite, akmlist,
sizeof(uint32_t) * akmsuiteCount);
} else {
hdd_err("Invalid akmSuite count: %u",
akmsuiteCount);
QDF_ASSERT(0);
return -EINVAL;
}
if (WAPI_PSK_AKM_SUITE == akmsuite[0]) {
hdd_debug("WAPI AUTH MODE SET TO PSK");
adapter->wapi_info.wapi_auth_mode =
WAPI_AUTH_MODE_PSK;
}
if (WAPI_CERT_AKM_SUITE == akmsuite[0]) {
hdd_debug("WAPI AUTH MODE SET TO CERTIFICATE");
adapter->wapi_info.wapi_auth_mode =
WAPI_AUTH_MODE_CERT;
}
hdd_set_crypto_key_mgmt_param(adapter);
break;
#endif
case DOT11F_EID_SUPPOPERATINGCLASSES:
{
hdd_debug("Set Supported Operating Classes IE(len %d)", eLen + 2);
status = wlan_hdd_add_assoc_ie(adapter,
genie - 2, eLen + 2);
if (status)
return status;
break;
}
case WLAN_REQUEST_IE_MAX_LEN:
{
if (genie[0] == SIR_FILS_HLP_EXT_EID) {
hdd_debug("Set HLP EXT IE(len %d)",
eLen + 2);
wlan_hdd_save_hlp_ie(roam_profile,
genie - 2, eLen + 2,
true);
status = wlan_hdd_add_assoc_ie(
adapter, genie - 2,
eLen + 2);
if (status)
return status;
} else if (genie[0] ==
SIR_DH_PARAMETER_ELEMENT_EXT_EID) {
hdd_debug("Set DH EXT IE(len %d)",
eLen + 2);
status = wlan_hdd_add_assoc_ie(
adapter, genie - 2,
eLen + 2);
if (status)
return status;
} else {
hdd_err("UNKNOWN EID: %X", genie[0]);
}
break;
}
case DOT11F_EID_FRAGMENT_IE:
{
hdd_debug("Set Fragment IE(len %d)", eLen + 2);
wlan_hdd_save_hlp_ie(roam_profile,
genie - 2, eLen + 2,
false);
status = wlan_hdd_add_assoc_ie(adapter,
genie - 2, eLen + 2);
if (status)
return status;
break;
}
default:
hdd_err("Set UNKNOWN IE: %X", elementId);
/* when Unknown IE is received we break
* and continue to the next IE in the buffer
*/
break;
}
genie += eLen;
remLen -= eLen;
}
return 0;
}
/**
* hdd_is_wpaie_present() - check for WPA ie
* @ie: Pointer to ie
* @ie_len: Ie length
*
* Parse the received IE to find the WPA IE
*
* Return: true if wpa ie is found else false
*/
static bool hdd_is_wpaie_present(const uint8_t *ie, uint8_t ie_len)
{
uint8_t eLen = 0;
uint16_t remLen = ie_len;
uint8_t elementId = 0;
while (remLen >= 2) {
elementId = *ie++;
eLen = *ie++;
remLen -= 2;
if (eLen > remLen) {
hdd_err("Invalid IE length: %d", eLen);
return false;
}
if ((elementId == DOT11F_EID_WPA) && (remLen > 5)) {
/* OUI - 0x00 0X50 0XF2
* WPA Information Element - 0x01
* WPA version - 0x01
*/
if (0 == memcmp(&ie[0], "\x00\x50\xf2\x01\x01", 5))
return true;
}
ie += eLen;
remLen -= eLen;
}
return false;
}
#ifdef CRYPTO_SET_KEY_CONVERGED
static void wlan_hdd_cfg80211_store_wep_key(struct hdd_adapter *adapter,
struct wlan_objmgr_vdev *vdev,
struct cfg80211_connect_params *req)
{
struct key_params params;
qdf_mem_zero(&params, sizeof(params));
params.cipher = req->crypto.ciphers_pairwise[0];
params.key_len = req->key_len;
params.key = req->key;
wlan_cfg80211_store_key(vdev, req->key_idx,
WLAN_CRYPTO_KEY_TYPE_UNICAST,
NULL, &params);
}
#else
static void wlan_hdd_cfg80211_store_wep_key(struct hdd_adapter *adapter,
struct wlan_objmgr_vdev *vdev,
struct cfg80211_connect_params *req)
{
struct csr_roam_profile *roam_profile;
roam_profile = hdd_roam_profile(adapter);
hdd_debug("setting default wep key, key_idx = %hu key_len %hu",
req->key_idx, req->key_len);
qdf_mem_copy(&roam_profile->Keys.KeyMaterial[req->key_idx][0],
req->key, req->key_len);
roam_profile->Keys.KeyLength[req->key_idx] = req->key_len;
roam_profile->Keys.defaultIndex = req->key_idx;
}
#endif /* !CRYPTO_SET_KEY_CONVERGED */
/**
* wlan_hdd_cfg80211_set_privacy() - set security parameters during connection
* @adapter: Pointer to adapter
* @req: Pointer to security parameters
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_privacy(struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
int status = 0;
struct hdd_station_ctx *sta_ctx;
struct csr_roam_profile *roam_profile;
struct wlan_objmgr_vdev *vdev;
hdd_enter();
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
sta_ctx->wpa_versions = req->crypto.wpa_versions;
hdd_debug("set wpa version to %d", sta_ctx->wpa_versions);
roam_profile = hdd_roam_profile(adapter);
/* populate auth,akm and cipher params for crypto */
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
hdd_populate_crypto_params(vdev, req);
/*set authentication type */
status = wlan_hdd_cfg80211_set_auth_type(adapter, req->auth_type);
if (wlan_hdd_is_conn_type_fils(req)) {
status = wlan_hdd_cfg80211_set_fils_config(adapter, req);
if (0 > status) {
hdd_err("Failed to set fils config");
goto release_vdev_ref;
}
}
/*set key mgmt type */
if (req->crypto.n_akm_suites) {
status =
wlan_hdd_set_akm_suite(adapter, req->crypto.akm_suites[0]);
if (0 > status) {
hdd_err("Failed to set akm suite");
goto release_vdev_ref;
}
}
/*set pairwise cipher type */
if (req->crypto.n_ciphers_pairwise) {
status = wlan_hdd_cfg80211_set_cipher(adapter,
req->crypto.
ciphers_pairwise[0],
true);
if (0 > status) {
hdd_err("Failed to set unicast cipher type");
goto release_vdev_ref;
}
} else {
/*Reset previous cipher suite to none */
status = wlan_hdd_cfg80211_set_cipher(adapter, 0, true);
if (0 > status) {
hdd_err("Failed to set unicast cipher type");
goto release_vdev_ref;
}
}
/*set group cipher type */
status =
wlan_hdd_cfg80211_set_cipher(adapter, req->crypto.cipher_group,
false);
if (0 > status) {
hdd_err("Failed to set mcast cipher type");
goto release_vdev_ref;
}
#ifdef WLAN_FEATURE_11W
roam_profile->MFPEnabled = (req->mfp == NL80211_MFP_REQUIRED);
#endif
/*parse WPA/RSN IE, and set the correspoing fileds in Roam profile */
if (req->ie_len) {
status =
wlan_hdd_cfg80211_set_ie(adapter, req->ie, req->ie_len);
if (0 > status) {
hdd_err("Failed to parse the WPA/RSN IE");
goto release_vdev_ref;
}
}
/*incase of WEP set default key information */
if (req->key && req->key_len) {
u32 cipher = req->crypto.ciphers_pairwise[0];
if ((WLAN_CIPHER_SUITE_WEP40 == cipher) ||
(WLAN_CIPHER_SUITE_WEP104 == cipher)) {
enum hdd_auth_key_mgmt key_mgmt =
sta_ctx->auth_key_mgmt;
if (key_mgmt & HDD_AUTH_KEY_MGMT_802_1X) {
hdd_err("Dynamic WEP not supported");
status = -EOPNOTSUPP;
goto release_vdev_ref;
}
if ((eCSR_SECURITY_WEP_KEYSIZE_MAX_BYTES >=
req->key_len) && (CSR_MAX_NUM_KEY > req->key_idx))
wlan_hdd_cfg80211_store_wep_key(adapter,
vdev, req);
}
}
release_vdev_ref:
hdd_objmgr_put_vdev(vdev);
return status;
}
/**
* wlan_hdd_clear_wapi_privacy() - reset WAPI settings in HDD layer
* @adapter: pointer to HDD adapter object
*
* This function resets all WAPI related parameters imposed before STA
* connection starts. It's invoked when privacy checking against concurrency
* fails, to make sure no improper WAPI settings are still populated before
* returning an error to the upper layer requester.
*
* Return: none
*/
#ifdef FEATURE_WLAN_WAPI
static inline void wlan_hdd_clear_wapi_privacy(struct hdd_adapter *adapter)
{
adapter->wapi_info.wapi_mode = 0;
adapter->wapi_info.wapi_auth_mode = WAPI_AUTH_MODE_OPEN;
}
#else
static inline void wlan_hdd_clear_wapi_privacy(struct hdd_adapter *adapter)
{
}
#endif
/**
* wlan_hdd_cfg80211_clear_privacy() - reset STA security parameters
* @adapter: pointer to HDD adapter object
*
* This function resets all privacy related parameters imposed
* before STA connection starts. It's invoked when privacy checking
* against concurrency fails, to make sure no improper settings are
* still populated before returning an error to the upper layer requester.
*
* Return: none
*/
static void wlan_hdd_cfg80211_clear_privacy(struct hdd_adapter *adapter)
{
struct hdd_station_ctx *hdd_sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
hdd_debug("resetting all privacy configurations");
hdd_sta_ctx->wpa_versions = 0;
hdd_sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_NONE;
hdd_sta_ctx->roam_profile.AuthType.authType[0] = eCSR_AUTH_TYPE_NONE;
hdd_sta_ctx->conn_info.uc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE;
hdd_sta_ctx->roam_profile.EncryptionType.numEntries = 0;
hdd_sta_ctx->conn_info.mc_encrypt_type = eCSR_ENCRYPT_TYPE_NONE;
hdd_sta_ctx->roam_profile.mcEncryptionType.numEntries = 0;
wlan_hdd_clear_wapi_privacy(adapter);
}
int wlan_hdd_try_disconnect(struct hdd_adapter *adapter)
{
unsigned long rc;
struct hdd_station_ctx *sta_ctx;
int status, result = 0;
mac_handle_t mac_handle;
uint32_t wait_time = SME_DISCONNECT_TIMEOUT;
struct hdd_context *hdd_ctx;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
mac_handle = hdd_adapter_get_mac_handle(adapter);
if (adapter->device_mode == QDF_STA_MODE) {
hdd_debug("Stop firmware roaming");
sme_stop_roaming(mac_handle, adapter->vdev_id,
eCsrForcedDisassoc);
/*
* If firmware has already started roaming process, driver
* needs to wait for processing of this disconnect request.
*
*/
INIT_COMPLETION(adapter->roaming_comp_var);
if (hdd_is_roaming_in_progress(hdd_ctx)) {
rc = wait_for_completion_timeout(
&adapter->roaming_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_STOP_ROAM));
if (!rc) {
hdd_err("roaming comp var timed out vdev id: %d",
adapter->vdev_id);
}
if (adapter->roam_ho_fail) {
INIT_COMPLETION(adapter->disconnect_comp_var);
hdd_conn_set_connection_state(adapter,
eConnectionState_Disconnecting);
}
}
}
if ((QDF_IBSS_MODE == adapter->device_mode) ||
(eConnectionState_Associated == sta_ctx->conn_info.conn_state) ||
(eConnectionState_Connecting == sta_ctx->conn_info.conn_state) ||
(eConnectionState_IbssConnected == sta_ctx->conn_info.conn_state)) {
eConnectionState prev_conn_state;
prev_conn_state = sta_ctx->conn_info.conn_state;
hdd_conn_set_connection_state(adapter,
eConnectionState_Disconnecting);
/* Issue disconnect to CSR */
INIT_COMPLETION(adapter->disconnect_comp_var);
status = sme_roam_disconnect(mac_handle,
adapter->vdev_id,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
if ((status == QDF_STATUS_CMD_NOT_QUEUED) &&
prev_conn_state != eConnectionState_Connecting) {
hdd_debug("Already disconnect in progress");
result = 0;
/*
* Wait here instead of returning directly. This will
* block the connect command and allow processing
* of the disconnect in SME. As disconnect is already
* in progress, wait here for 1 sec instead of 5 sec.
*/
wait_time = WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS;
} else if (status == QDF_STATUS_CMD_NOT_QUEUED) {
/*
* Wait here instead of returning directly, this will
* block the connect command and allow processing
* of the scan for ssid and the previous connect command
* in CSR.
*/
hdd_debug("Already disconnected or connect was in sme/roam pending list and removed by disconnect");
} else if (0 != status) {
hdd_err("sme_roam_disconnect failure, status: %d",
(int)status);
sta_ctx->sta_debug_state = status;
result = -EINVAL;
goto disconnected;
}
rc = wait_for_completion_timeout(&adapter->disconnect_comp_var,
msecs_to_jiffies(wait_time));
if (!rc && (QDF_STATUS_CMD_NOT_QUEUED != status)) {
hdd_err("Sme disconnect event timed out vdev id: %d sta_debug_state: %d",
adapter->vdev_id, sta_ctx->sta_debug_state);
result = -ETIMEDOUT;
}
} else if (eConnectionState_Disconnecting ==
sta_ctx->conn_info.conn_state) {
rc = wait_for_completion_timeout(&adapter->disconnect_comp_var,
msecs_to_jiffies(wait_time));
if (!rc) {
hdd_err("Disconnect event timed out vdev id: %d sta_debug_state: %d",
adapter->vdev_id, sta_ctx->sta_debug_state);
result = -ETIMEDOUT;
}
}
disconnected:
hdd_conn_set_connection_state(adapter, eConnectionState_NotConnected);
return result;
}
/**
* wlan_hdd_reassoc_bssid_hint() - Start reassociation if bssid is present
* @adapter: Pointer to the HDD adapter
* @req: Pointer to the structure cfg_connect_params receieved from user space
*
* This function will start reassociation if prev_bssid is set and bssid/
* bssid_hint, channel/channel_hint parameters are present in connect request.
*
* Return: 0 if connect was for ReAssociation, non-zero error code otherwise
*/
#if defined(CFG80211_CONNECT_PREV_BSSID) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
static int wlan_hdd_reassoc_bssid_hint(struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
int status = -EINVAL;
const uint8_t *bssid = NULL;
uint16_t channel = 0;
struct hdd_station_ctx *sta_ctx;
if (req->bssid)
bssid = req->bssid;
else if (req->bssid_hint)
bssid = req->bssid_hint;
if (req->channel)
channel = req->channel->hw_value;
else if (req->channel_hint)
channel = req->channel_hint->hw_value;
if (bssid && channel && req->prev_bssid) {
hdd_debug("REASSOC Attempt on channel %d to " QDF_MAC_ADDR_STR,
channel, QDF_MAC_ADDR_ARRAY(bssid));
/*
* Save BSSID in a separate variable as
* roam_profile's BSSID is getting zeroed out in the
* association process. In case of join failure
* we should send valid BSSID to supplicant
*/
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
qdf_mem_copy(sta_ctx->requested_bssid.bytes, bssid,
QDF_MAC_ADDR_SIZE);
status = hdd_reassoc(adapter, bssid, channel,
CONNECT_CMD_USERSPACE);
hdd_debug("hdd_reassoc: status: %d", status);
}
return status;
}
#else
static int wlan_hdd_reassoc_bssid_hint(struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
return -ENOTSUPP;
}
#endif
/**
* wlan_hdd_check_ht20_ht40_ind() - check if Supplicant has indicated to
* connect in HT20 mode
* @hdd_ctx: hdd context
* @adapter: Pointer to the HDD adapter
* @req: Pointer to the structure cfg_connect_params receieved from user space
*
* This function will check if supplicant has indicated to to connect in HT20
* mode. this is currently applicable only for 2.4Ghz mode only.
* if feature is enabled and supplicant indicate HT20 set
* force_24ghz_in_ht20 to true to force 2.4Ghz in HT20 else set it to false.
*
* Return: void
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
static void
wlan_hdd_check_ht20_ht40_ind(struct hdd_context *hdd_ctx,
struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
struct csr_roam_profile *roam_profile;
bool is_override_ht20_40_24g;
roam_profile = hdd_roam_profile(adapter);
roam_profile->force_24ghz_in_ht20 = false;
ucfg_mlme_is_override_ht20_40_24g(hdd_ctx->psoc,
&is_override_ht20_40_24g);
if (is_override_ht20_40_24g &&
!(req->ht_capa.cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
roam_profile->force_24ghz_in_ht20 = true;
hdd_debug("req->ht_capa.cap_info %x override_ht20_40_24g %d",
req->ht_capa.cap_info,
is_override_ht20_40_24g);
}
#else
static inline void
wlan_hdd_check_ht20_ht40_ind(struct hdd_context *hdd_ctx,
struct hdd_adapter *adapter,
struct cfg80211_connect_params *req)
{
struct csr_roam_profile *roam_profile;
roam_profile = hdd_roam_profile(adapter);
roam_profile->force_24ghz_in_ht20 = false;
}
#endif
/**
* __wlan_hdd_cfg80211_connect() - cfg80211 connect api
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @req: Pointer to cfg80211 connect request
*
* This function is used to start the association process
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *req)
{
int status;
u16 channel, sap_cnt, sta_cnt;
const u8 *bssid = NULL;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
const u8 *bssid_hint = req->bssid_hint;
#else
const u8 *bssid_hint = NULL;
#endif
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(ndev);
struct hdd_context *hdd_ctx;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CONNECT,
adapter->vdev_id, adapter->device_mode);
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
if (adapter->device_mode != QDF_STA_MODE &&
adapter->device_mode != QDF_P2P_CLIENT_MODE) {
hdd_err("Device_mode %s(%d) is not supported",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode);
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (!hdd_ctx) {
hdd_err("HDD context is null");
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
/*
* Disable NAN Discovery if incoming connection is P2P or if a STA
* connection already exists and if this is a case of STA+STA
* or SAP+STA concurrency
*/
sta_cnt = policy_mgr_mode_specific_connection_count(hdd_ctx->psoc,
PM_STA_MODE, NULL);
sap_cnt = policy_mgr_mode_specific_connection_count(hdd_ctx->psoc,
PM_SAP_MODE, NULL);
if (adapter->device_mode == QDF_P2P_CLIENT_MODE || sap_cnt || sta_cnt) {
hdd_debug("Invalid NAN concurrency. SAP: %d STA: %d P2P: %d",
sap_cnt, sta_cnt,
(adapter->device_mode == QDF_P2P_CLIENT_MODE));
ucfg_nan_disable_concurrency(hdd_ctx->psoc);
}
/*
* STA+NDI concurrency gets preference over NDI+NDI. Disable
* first NDI in case an NDI+NDI concurrency exists.
*/
ucfg_nan_check_and_disable_unsupported_ndi(hdd_ctx->psoc);
if (req->bssid)
bssid = req->bssid;
else if (bssid_hint)
bssid = bssid_hint;
if (bssid && hdd_get_adapter_by_macaddr(hdd_ctx, (uint8_t *)bssid)) {
hdd_err("adapter exist with same mac address " QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(bssid));
return -EINVAL;
}
/*
* Check if this is reassoc to same bssid, if reassoc is success, return
*/
status = wlan_hdd_reassoc_bssid_hint(adapter, req);
if (!status) {
hdd_set_roaming_in_progress(true);
return status;
}
/* Try disconnecting if already in connected state */
status = wlan_hdd_try_disconnect(adapter);
if (0 > status) {
hdd_err("Failed to disconnect the existing connection");
return -EALREADY;
}
/*initialise security parameters */
status = wlan_hdd_cfg80211_set_privacy(adapter, req);
if (status < 0) {
hdd_err("Failed to set security params");
return status;
}
/*
* Check for max concurrent connections after doing disconnect if any,
* must be called after the invocation of wlan_hdd_cfg80211_set_privacy
* so privacy is already set for the current adapter before it's
* checked against concurrency.
*/
if (req->channel) {
bool ok = false;
if (req->channel->hw_value && policy_mgr_is_chan_ok_for_dnbs(
hdd_ctx->psoc,
req->channel->hw_value,
&ok)) {
hdd_warn("Unable to get channel:%d eligibility for DNBS",
req->channel->hw_value);
return -EINVAL;
}
/**
* Send connection timedout, so that Android framework does not
* blacklist us.
*/
if (!ok) {
struct ieee80211_channel *chan =
ieee80211_get_channel(wiphy,
wlan_chan_to_freq(req->channel->hw_value));
struct cfg80211_bss *bss;
hdd_warn("Channel:%d not OK for DNBS",
req->channel->hw_value);
if (chan) {
bss = wlan_cfg80211_get_bss(wiphy, chan,
req->bssid,
req->ssid,
req->ssid_len);
if (bss) {
cfg80211_assoc_timeout(ndev, bss);
return -ETIMEDOUT;
}
}
return -EINVAL;
}
if (!policy_mgr_allow_concurrency(hdd_ctx->psoc,
policy_mgr_convert_device_mode_to_qdf_type(
adapter->device_mode),
req->channel->hw_value, HW_MODE_20_MHZ)) {
hdd_warn("This concurrency combination is not allowed");
status = -ECONNREFUSED;
goto con_chk_failed;
}
} else {
if (!policy_mgr_allow_concurrency(hdd_ctx->psoc,
policy_mgr_convert_device_mode_to_qdf_type(
adapter->device_mode), 0, HW_MODE_20_MHZ)) {
hdd_warn("This concurrency combination is not allowed");
status = -ECONNREFUSED;
goto con_chk_failed;
}
}
if (req->channel)
channel = req->channel->hw_value;
else
channel = 0;
wlan_hdd_check_ht20_ht40_ind(hdd_ctx, adapter, req);
status = wlan_hdd_cfg80211_connect_start(adapter, req->ssid,
req->ssid_len, req->bssid,
bssid_hint, channel, 0);
if (0 > status)
hdd_err("connect failed");
return status;
con_chk_failed:
wlan_hdd_cfg80211_clear_privacy(adapter);
hdd_exit();
return status;
}
/**
* wlan_hdd_cfg80211_connect() - cfg80211 connect api
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @req: Pointer to cfg80211 connect request
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *req)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(ndev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_connect(wiphy, ndev, req);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
int wlan_hdd_disconnect(struct hdd_adapter *adapter, u16 reason)
{
QDF_STATUS status;
int result = 0;
unsigned long rc;
struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
eConnectionState prev_conn_state;
mac_handle_t mac_handle;
uint32_t wait_time = SME_DISCONNECT_TIMEOUT;
hdd_enter();
mac_handle = hdd_ctx->mac_handle;
if (adapter->device_mode == QDF_STA_MODE) {
hdd_debug("Stop firmware roaming");
status = sme_stop_roaming(mac_handle, adapter->vdev_id,
eCsrForcedDisassoc);
/*
* If firmware has already started roaming process, driver
* needs to wait for processing of this disconnect request.
*
*/
INIT_COMPLETION(adapter->roaming_comp_var);
if (hdd_is_roaming_in_progress(hdd_ctx)) {
rc = wait_for_completion_timeout(
&adapter->roaming_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_STOP_ROAM));
if (!rc) {
hdd_err("roaming comp var timed out vdev id: %d",
adapter->vdev_id);
}
if (adapter->roam_ho_fail) {
INIT_COMPLETION(adapter->disconnect_comp_var);
hdd_debug("Disabling queues");
wlan_hdd_netif_queue_control(adapter,
WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER,
WLAN_CONTROL_PATH);
hdd_conn_set_connection_state(adapter,
eConnectionState_Disconnecting);
goto wait_for_disconnect;
}
}
}
prev_conn_state = sta_ctx->conn_info.conn_state;
/*stop tx queues */
hdd_info("Disabling queues");
wlan_hdd_netif_queue_control(adapter,
WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER, WLAN_CONTROL_PATH);
hdd_debug("Set HDD conn_state to eConnectionState_Disconnecting");
hdd_conn_set_connection_state(adapter, eConnectionState_Disconnecting);
INIT_COMPLETION(adapter->disconnect_comp_var);
/* issue disconnect */
status = sme_roam_disconnect(mac_handle,
adapter->vdev_id, reason);
if ((QDF_STATUS_CMD_NOT_QUEUED == status) &&
prev_conn_state != eConnectionState_Connecting) {
hdd_debug("status = %d, already disconnected", status);
result = 0;
/*
* Wait here instead of returning directly. This will block the
* next connect command and allow processing of the disconnect
* in SME else we might hit some race conditions leading to SME
* and HDD out of sync. As disconnect is already in progress,
* wait here for 1 sec instead of 5 sec.
*/
wait_time = WLAN_WAIT_DISCONNECT_ALREADY_IN_PROGRESS;
} else if (QDF_STATUS_CMD_NOT_QUEUED == status) {
/*
* Wait here instead of returning directly, this will block the
* next connect command and allow processing of the scan for
* ssid and the previous connect command in CSR. Else we might
* hit some race conditions leading to SME and HDD out of sync.
*/
hdd_debug("Already disconnected or connect was in sme/roam pending list and removed by disconnect");
} else if (0 != status) {
hdd_err("csr_roam_disconnect failure, status: %d", (int)status);
sta_ctx->sta_debug_state = status;
result = -EINVAL;
goto disconnected;
}
wait_for_disconnect:
rc = wait_for_completion_timeout(&adapter->disconnect_comp_var,
msecs_to_jiffies(wait_time));
if (!rc && (QDF_STATUS_CMD_NOT_QUEUED != status)) {
hdd_err("Failed to disconnect, timed out");
result = -ETIMEDOUT;
}
disconnected:
hdd_conn_set_connection_state(adapter, eConnectionState_NotConnected);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
/* Sending disconnect event to userspace for kernel version < 3.11
* is handled by __cfg80211_disconnect call to __cfg80211_disconnected
*/
hdd_debug("Send disconnected event to userspace");
wlan_hdd_cfg80211_indicate_disconnect(adapter->dev, true,
WLAN_REASON_UNSPECIFIED);
#endif
return result;
}
/**
* hdd_ieee80211_reason_code_to_str() - return string conversion of reason code
* @reason: ieee80211 reason code.
*
* This utility function helps log string conversion of reason code.
*
* Return: string conversion of reason code, if match found;
* "Unknown" otherwise.
*/
static const char *hdd_ieee80211_reason_code_to_str(uint16_t reason)
{
switch (reason) {
CASE_RETURN_STRING(WLAN_REASON_UNSPECIFIED);
CASE_RETURN_STRING(WLAN_REASON_PREV_AUTH_NOT_VALID);
CASE_RETURN_STRING(WLAN_REASON_DEAUTH_LEAVING);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_AP_BUSY);
CASE_RETURN_STRING(WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
CASE_RETURN_STRING(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_STA_HAS_LEFT);
CASE_RETURN_STRING(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_BAD_POWER);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_BAD_SUPP_CHAN);
CASE_RETURN_STRING(WLAN_REASON_INVALID_IE);
CASE_RETURN_STRING(WLAN_REASON_MIC_FAILURE);
CASE_RETURN_STRING(WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT);
CASE_RETURN_STRING(WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT);
CASE_RETURN_STRING(WLAN_REASON_IE_DIFFERENT);
CASE_RETURN_STRING(WLAN_REASON_INVALID_GROUP_CIPHER);
CASE_RETURN_STRING(WLAN_REASON_INVALID_PAIRWISE_CIPHER);
CASE_RETURN_STRING(WLAN_REASON_INVALID_AKMP);
CASE_RETURN_STRING(WLAN_REASON_UNSUPP_RSN_VERSION);
CASE_RETURN_STRING(WLAN_REASON_INVALID_RSN_IE_CAP);
CASE_RETURN_STRING(WLAN_REASON_IEEE8021X_FAILED);
CASE_RETURN_STRING(WLAN_REASON_CIPHER_SUITE_REJECTED);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_UNSPECIFIED_QOS);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_QAP_NO_BANDWIDTH);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_LOW_ACK);
CASE_RETURN_STRING(WLAN_REASON_DISASSOC_QAP_EXCEED_TXOP);
CASE_RETURN_STRING(WLAN_REASON_QSTA_LEAVE_QBSS);
CASE_RETURN_STRING(WLAN_REASON_QSTA_NOT_USE);
CASE_RETURN_STRING(WLAN_REASON_QSTA_REQUIRE_SETUP);
CASE_RETURN_STRING(WLAN_REASON_QSTA_TIMEOUT);
CASE_RETURN_STRING(WLAN_REASON_QSTA_CIPHER_NOT_SUPP);
CASE_RETURN_STRING(WLAN_REASON_MESH_PEER_CANCELED);
CASE_RETURN_STRING(WLAN_REASON_MESH_MAX_PEERS);
CASE_RETURN_STRING(WLAN_REASON_MESH_CONFIG);
CASE_RETURN_STRING(WLAN_REASON_MESH_CLOSE);
CASE_RETURN_STRING(WLAN_REASON_MESH_MAX_RETRIES);
CASE_RETURN_STRING(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
CASE_RETURN_STRING(WLAN_REASON_MESH_INVALID_GTK);
CASE_RETURN_STRING(WLAN_REASON_MESH_INCONSISTENT_PARAM);
CASE_RETURN_STRING(WLAN_REASON_MESH_INVALID_SECURITY);
CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_ERROR);
CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_NOFORWARD);
CASE_RETURN_STRING(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE);
CASE_RETURN_STRING(WLAN_REASON_MAC_EXISTS_IN_MBSS);
CASE_RETURN_STRING(WLAN_REASON_MESH_CHAN_REGULATORY);
CASE_RETURN_STRING(WLAN_REASON_MESH_CHAN);
default:
return "Unknown";
}
}
/**
* __wlan_hdd_cfg80211_disconnect() - cfg80211 disconnect api
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @reason: Disconnect reason code
*
* This function is used to issue a disconnect request to SME
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy,
struct net_device *dev, u16 reason)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
int status;
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct wlan_objmgr_vdev *vdev;
bool enable_deauth_to_disassoc_map;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DISCONNECT,
adapter->vdev_id, reason);
hdd_print_netdev_txq_status(dev);
hdd_debug("Device_mode %s(%d) reason code(%d)",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode, reason);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
qdf_mutex_acquire(&adapter->disconnection_status_lock);
if (adapter->disconnection_in_progress) {
qdf_mutex_release(&adapter->disconnection_status_lock);
hdd_debug("Disconnect is already in progress");
return 0;
}
adapter->disconnection_in_progress = true;
qdf_mutex_release(&adapter->disconnection_status_lock);
/* Issue disconnect request to SME, if station is in connected state */
if ((sta_ctx->conn_info.conn_state == eConnectionState_Associated) ||
(sta_ctx->conn_info.conn_state == eConnectionState_Connecting)) {
eCsrRoamDisconnectReason reasonCode =
eCSR_DISCONNECT_REASON_UNSPECIFIED;
switch (reason) {
case WLAN_REASON_MIC_FAILURE:
reasonCode = eCSR_DISCONNECT_REASON_MIC_ERROR;
break;
case WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY:
case WLAN_REASON_DISASSOC_AP_BUSY:
case WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA:
reasonCode = eCSR_DISCONNECT_REASON_DISASSOC;
break;
case WLAN_REASON_PREV_AUTH_NOT_VALID:
case WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA:
reasonCode = eCSR_DISCONNECT_REASON_DEAUTH;
break;
case WLAN_REASON_DEAUTH_LEAVING:
status = ucfg_mlme_get_enable_deauth_to_disassoc_map(
hdd_ctx->psoc,
&enable_deauth_to_disassoc_map);
if (QDF_IS_STATUS_ERROR(status))
return -EINVAL;
reasonCode =
enable_deauth_to_disassoc_map ?
eCSR_DISCONNECT_REASON_STA_HAS_LEFT :
eCSR_DISCONNECT_REASON_DEAUTH;
qdf_dp_trace_dump_all(
WLAN_DEAUTH_DPTRACE_DUMP_COUNT,
QDF_TRACE_DEFAULT_PDEV_ID);
break;
case WLAN_REASON_DISASSOC_STA_HAS_LEFT:
reasonCode = eCSR_DISCONNECT_REASON_STA_HAS_LEFT;
break;
default:
reasonCode = eCSR_DISCONNECT_REASON_UNSPECIFIED;
break;
}
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return -EINVAL;
if (ucfg_scan_get_vdev_status(vdev) !=
SCAN_NOT_IN_PROGRESS) {
hdd_debug("Disconnect is in progress, Aborting Scan");
wlan_abort_scan(hdd_ctx->pdev, INVAL_PDEV_ID,
adapter->vdev_id, INVALID_SCAN_ID,
false);
}
wlan_hdd_cleanup_remain_on_channel_ctx(adapter);
/* First clean up the tdls peers if any */
hdd_notify_sta_disconnect(adapter->vdev_id,
false, true, vdev);
hdd_objmgr_put_vdev(vdev);
hdd_info("Disconnect from userspace; reason:%d (%s)",
reason, hdd_ieee80211_reason_code_to_str(reason));
status = wlan_hdd_disconnect(adapter, reasonCode);
if (0 != status) {
hdd_err("wlan_hdd_disconnect failed, status: %d", status);
hdd_set_disconnect_status(adapter, false);
return -EINVAL;
}
} else {
hdd_err("Unexpected cfg disconnect called while in state: %d",
sta_ctx->conn_info.conn_state);
hdd_set_disconnect_status(adapter, false);
}
return status;
}
/**
* wlan_hdd_cfg80211_disconnect() - cfg80211 disconnect api
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @reason: Disconnect reason code
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_disconnect(struct wiphy *wiphy,
struct net_device *dev, u16 reason)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_disconnect(wiphy, dev, reason);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* wlan_hdd_cfg80211_set_privacy_ibss() - set ibss privacy
* @adapter: Pointer to adapter
* @param: Pointer to IBSS parameters
*
* This function is used to initialize the security settings in IBSS mode
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_privacy_ibss(struct hdd_adapter *adapter,
struct cfg80211_ibss_params
*params)
{
uint32_t ret;
int status = 0;
eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE;
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct csr_roam_profile *roam_profile;
hdd_enter();
sta_ctx->wpa_versions = 0;
qdf_mem_zero(&sta_ctx->ibss_enc_key, sizeof(tCsrRoamSetKey));
sta_ctx->ibss_enc_key_installed = 0;
if (params->ie_len && (params->ie)) {
if (wlan_get_ie_ptr_from_eid(WLAN_EID_RSN, params->ie,
params->ie_len)) {
sta_ctx->wpa_versions = NL80211_WPA_VERSION_2;
encryptionType = eCSR_ENCRYPT_TYPE_AES;
} else if (hdd_is_wpaie_present(params->ie, params->ie_len)) {
tDot11fIEWPA dot11_wpa_ie;
mac_handle_t mac_handle =
hdd_adapter_get_mac_handle(adapter);
const u8 *ie;
memset(&dot11_wpa_ie, 0, sizeof(dot11_wpa_ie));
ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_WPA,
params->ie, params->ie_len);
if (ie) {
sta_ctx->wpa_versions = NL80211_WPA_VERSION_1;
/* Unpack the WPA IE
* Skip past the EID byte and length byte
* and four byte WiFi OUI
*/
if (ie[1] < DOT11F_IE_WPA_MIN_LEN ||
ie[1] > DOT11F_IE_WPA_MAX_LEN) {
hdd_err("invalid ie len:%d", ie[1]);
return -EINVAL;
}
ret = dot11f_unpack_ie_wpa(
MAC_CONTEXT(mac_handle),
(uint8_t *)&ie[2 + 4],
ie[1] - 4, &dot11_wpa_ie, false);
if (DOT11F_FAILED(ret)) {
hdd_err("unpack failed ret: 0x%x", ret);
return -EINVAL;
}
/*
* Extract the multicast cipher, the
* encType for unicast cipher for
* wpa-none is none
*/
encryptionType =
hdd_translate_wpa_to_csr_encryption_type
(dot11_wpa_ie.multicast_cipher);
}
}
status =
wlan_hdd_cfg80211_set_ie(adapter, params->ie,
params->ie_len);
if (0 > status) {
hdd_err("Failed to parse WPA/RSN IE");
return status;
}
}
roam_profile = hdd_roam_profile(adapter);
roam_profile->AuthType.authType[0] =
sta_ctx->conn_info.auth_type = eCSR_AUTH_TYPE_OPEN_SYSTEM;
if (params->privacy) {
/* Security enabled IBSS, At this time there is no information
* available about the security parameters, so initialise the
* encryption type to eCSR_ENCRYPT_TYPE_WEP40_STATICKEY.
* The correct security parameters will be updated later in
* wlan_hdd_cfg80211_add_key Hal expects encryption type to be
* set inorder enable privacy bit in beacons
*/
encryptionType = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY;
}
hdd_debug("encryptionType=%d", encryptionType);
sta_ctx->conn_info.uc_encrypt_type = encryptionType;
roam_profile->EncryptionType.numEntries = 1;
roam_profile->EncryptionType.encryptionType[0] =
encryptionType;
return status;
}
/**
* __wlan_hdd_cfg80211_join_ibss() - join ibss
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @param: Pointer to IBSS join parameters
*
* This function is used to create/join an IBSS network
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_join_ibss(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ibss_params *params)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct csr_roam_profile *roam_profile;
int status;
struct hdd_station_ctx *sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(adapter);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct qdf_mac_addr bssid;
u8 channelNum = 0;
mac_handle_t mac_handle;
struct wlan_mlme_ibss_cfg ibss_cfg = {0};
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_JOIN_IBSS,
adapter->vdev_id, adapter->device_mode);
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
if (QDF_IS_STATUS_ERROR(ucfg_mlme_get_ibss_cfg(hdd_ctx->psoc,
&ibss_cfg))) {
return -EINVAL;
}
mac_handle = hdd_ctx->mac_handle;
if (NULL !=
params->chandef.chan) {
uint32_t numChans = CFG_VALID_CHANNEL_LIST_LEN;
uint8_t validChan[CFG_VALID_CHANNEL_LIST_LEN];
int indx;
/* Get channel number */
channelNum = ieee80211_frequency_to_channel(
params->
chandef.
chan->
center_freq);
ucfg_mlme_get_valid_channel_list(hdd_ctx->psoc, validChan,
&numChans);
for (indx = 0; indx < numChans; indx++) {
if (channelNum == validChan[indx])
break;
}
if (indx >= numChans) {
hdd_err("Not valid Channel: %d", channelNum);
return -EINVAL;
}
}
/* Disable NAN Discovery if enabled */
ucfg_nan_disable_concurrency(hdd_ctx->psoc);
if (!policy_mgr_allow_concurrency(hdd_ctx->psoc,
PM_IBSS_MODE, channelNum, HW_MODE_20_MHZ)) {
hdd_err("This concurrency combination is not allowed");
return -ECONNREFUSED;
}
status = policy_mgr_reset_connection_update(hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("qdf_reset_connection_update failed status: %d", status);
status = policy_mgr_current_connections_update(hdd_ctx->psoc,
adapter->vdev_id, channelNum,
POLICY_MGR_UPDATE_REASON_JOIN_IBSS);
if (QDF_STATUS_E_FAILURE == status) {
hdd_err("connections update failed!!");
return -EINVAL;
}
if (QDF_STATUS_SUCCESS == status) {
status = policy_mgr_wait_for_connection_update(
hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("qdf wait for event failed!!");
return -EINVAL;
}
}
/*Try disconnecting if already in connected state */
status = wlan_hdd_try_disconnect(adapter);
if (0 > status) {
hdd_err("Failed to disconnect the existing IBSS connection");
return -EALREADY;
}
roam_profile = hdd_roam_profile(adapter);
if (eCSR_BSS_TYPE_START_IBSS != roam_profile->BSSType) {
hdd_err("Interface type is not set to IBSS");
return -EINVAL;
}
/* enable selected protection checks in IBSS mode */
roam_profile->cfg_protection = IBSS_CFG_PROTECTION_ENABLE_MASK;
/* BSSID is provided by upper layers hence no need to AUTO generate */
if (params->bssid) {
if (ucfg_mlme_set_ibss_auto_bssid(hdd_ctx->psoc, 0)
== QDF_STATUS_E_FAILURE) {
hdd_err("Unable to update MLME IBSS Auto BSSID config");
return -EIO;
}
qdf_mem_copy(bssid.bytes, params->bssid, QDF_MAC_ADDR_SIZE);
} else if (ibss_cfg.coalesing_enable == 0) {
if (ucfg_mlme_set_ibss_auto_bssid(hdd_ctx->psoc, 0)
== QDF_STATUS_E_FAILURE) {
hdd_err("Unable to update MLME IBSS Auto BSSID config");
return -EIO;
}
qdf_copy_macaddr(&bssid, &ibss_cfg.bssid);
}
if (cfg_in_range(CFG_BEACON_INTERVAL, params->beacon_interval))
roam_profile->beaconInterval = params->beacon_interval;
else
roam_profile->beaconInterval = cfg_get(hdd_ctx->psoc,
CFG_BEACON_INTERVAL);
/* Set Channel */
if (channelNum) {
/* Set the Operational Channel */
hdd_debug("set channel %d", channelNum);
roam_profile->ChannelInfo.numOfChannels = 1;
sta_ctx->conn_info.channel = channelNum;
roam_profile->ChannelInfo.ChannelList =
&sta_ctx->conn_info.channel;
}
/* Initialize security parameters */
status = wlan_hdd_cfg80211_set_privacy_ibss(adapter, params);
if (status < 0) {
hdd_err("failed to set security parameters");
return status;
}
/* Issue connect start */
status = wlan_hdd_cfg80211_connect_start(adapter, params->ssid,
params->ssid_len,
bssid.bytes, NULL,
sta_ctx->conn_info.
channel,
params->chandef.width);
if (0 > status) {
hdd_err("connect failed");
return status;
}
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_join_ibss() - join ibss
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @param: Pointer to IBSS join parameters
*
* This function is used to create/join an IBSS network
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_join_ibss(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ibss_params *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_join_ibss(wiphy, dev, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_leave_ibss() - leave ibss
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
*
* This function is used to leave an IBSS network
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_leave_ibss(struct wiphy *wiphy,
struct net_device *dev)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct csr_roam_profile *roam_profile;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int status;
mac_handle_t mac_handle;
unsigned long rc;
tSirUpdateIE update_ie;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_LEAVE_IBSS,
adapter->vdev_id, eCSR_DISCONNECT_REASON_IBSS_LEAVE);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
hdd_debug("Device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode), adapter->device_mode);
roam_profile = hdd_roam_profile(adapter);
/* Issue disconnect only if interface type is set to IBSS */
if (eCSR_BSS_TYPE_START_IBSS != roam_profile->BSSType) {
hdd_err("BSS Type is not set to IBSS");
return -EINVAL;
}
/* Clearing add IE of beacon */
qdf_mem_copy(update_ie.bssid.bytes, adapter->mac_addr.bytes,
sizeof(tSirMacAddr));
update_ie.smeSessionId = adapter->vdev_id;
update_ie.ieBufferlength = 0;
update_ie.pAdditionIEBuffer = NULL;
update_ie.append = true;
update_ie.notify = true;
mac_handle = hdd_ctx->mac_handle;
if (sme_update_add_ie(mac_handle,
&update_ie,
eUPDATE_IE_PROBE_BCN) == QDF_STATUS_E_FAILURE) {
hdd_err("Could not pass on PROBE_RSP_BCN data to PE");
}
/* Reset WNI_CFG_PROBE_RSP Flags */
wlan_hdd_reset_prob_rspies(adapter);
/* Issue Disconnect request */
INIT_COMPLETION(adapter->disconnect_comp_var);
status = sme_roam_disconnect(mac_handle,
adapter->vdev_id,
eCSR_DISCONNECT_REASON_IBSS_LEAVE);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("sme_roam_disconnect failed status: %d",
status);
return -EAGAIN;
}
/* wait for mc thread to cleanup and then return to upper stack
* so by the time upper layer calls the change interface, we are
* all set to proceed further
*/
rc = wait_for_completion_timeout(&adapter->disconnect_comp_var,
msecs_to_jiffies(SME_DISCONNECT_TIMEOUT));
if (!rc) {
hdd_err("Failed to disconnect, timed out");
return -ETIMEDOUT;
}
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_leave_ibss() - leave ibss
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
*
* This function is used to leave an IBSS network
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_leave_ibss(struct wiphy *wiphy,
struct net_device *dev)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_leave_ibss(wiphy, dev);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters
* @wiphy: Pointer to wiphy
* @changed: Parameters changed
*
* This function is used to set the phy parameters. RTS Threshold/FRAG
* Threshold/Retry Count etc.
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy,
u32 changed)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
int status;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS,
NO_SESSION, wiphy->rts_threshold);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
u32 rts_threshold = (wiphy->rts_threshold == -1) ?
cfg_max(CFG_RTS_THRESHOLD) :
wiphy->rts_threshold;
if ((cfg_min(CFG_RTS_THRESHOLD) > rts_threshold) ||
(cfg_max(CFG_RTS_THRESHOLD) < rts_threshold)) {
hdd_err("Invalid RTS Threshold value: %u",
rts_threshold);
return -EINVAL;
}
if (0 != ucfg_mlme_set_rts_threshold(hdd_ctx->psoc,
rts_threshold)) {
hdd_err("mlme_set_rts_threshold failed for val %u",
rts_threshold);
return -EIO;
}
hdd_debug("set rts threshold %u", rts_threshold);
}
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
u16 frag_threshold = (wiphy->frag_threshold == -1) ?
cfg_max(CFG_FRAG_THRESHOLD) :
wiphy->frag_threshold;
if ((cfg_min(CFG_FRAG_THRESHOLD) > frag_threshold) ||
(cfg_max(CFG_FRAG_THRESHOLD) < frag_threshold)) {
hdd_err("Invalid frag_threshold value %hu",
frag_threshold);
return -EINVAL;
}
if (0 != ucfg_mlme_set_frag_threshold(hdd_ctx->psoc,
frag_threshold)) {
hdd_err("mlme_set_frag_threshold failed for val %hu",
frag_threshold);
return -EIO;
}
hdd_debug("set frag threshold %hu", frag_threshold);
}
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_set_wiphy_params() - set wiphy parameters
* @wiphy: Pointer to wiphy
* @changed: Parameters changed
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_wiphy_params(wiphy, changed);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
/**
* __wlan_hdd_set_default_mgmt_key() - dummy implementation of set default mgmt
* key
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @key_index: Key index
*
* Return: 0
*/
static int __wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index)
{
hdd_enter();
return 0;
}
/**
* wlan_hdd_set_default_mgmt_key() - SSR wrapper for
* wlan_hdd_set_default_mgmt_key
* @wiphy: pointer to wiphy
* @netdev: pointer to net_device structure
* @key_index: key index
*
* Return: 0 on success, error number on failure
*/
static int wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(netdev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_set_default_mgmt_key(wiphy, netdev, key_index);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_set_txq_params() - dummy implementation of set tx queue params
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @params: Pointer to tx queue parameters
*
* Return: 0
*/
static int __wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_txq_params *params)
{
hdd_enter();
return 0;
}
/**
* wlan_hdd_set_txq_params() - SSR wrapper for wlan_hdd_set_txq_params
* @wiphy: pointer to wiphy
* @netdev: pointer to net_device structure
* @params: pointer to ieee80211_txq_params
*
* Return: 0 on success, error number on failure
*/
static int wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_txq_params *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_set_txq_params(wiphy, dev, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_del_station() - delete station v2
* @wiphy: Pointer to wiphy
* @dev: Underlying net device
* @param: Pointer to delete station parameter
*
* Return: 0 for success, non-zero for failure
*/
static
int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
struct csr_del_sta_params *param)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx;
QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
struct hdd_hostapd_state *hapd_state;
uint8_t sta_id;
uint8_t *mac;
mac_handle_t mac_handle;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DEL_STA,
adapter->vdev_id, adapter->device_mode);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (!hdd_ctx) {
hdd_err("hdd_ctx is NULL");
return -EINVAL;
}
mac = (uint8_t *) param->peerMacAddr.bytes;
mac_handle = hdd_ctx->mac_handle;
if ((QDF_SAP_MODE == adapter->device_mode) ||
(QDF_P2P_GO_MODE == adapter->device_mode)) {
hapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter);
if (!hapd_state) {
hdd_err("Hostapd State is Null");
return 0;
}
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *) mac)) {
uint16_t i;
bool is_sap_bcast_deauth_enabled = false;
ucfg_mlme_is_sap_bcast_deauth_enabled(
hdd_ctx->psoc,
&is_sap_bcast_deauth_enabled);
hdd_debug("is_sap_bcast_deauth_enabled %d",
is_sap_bcast_deauth_enabled);
if (is_sap_bcast_deauth_enabled)
goto fn_end;
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
if ((adapter->sta_info[i].in_use) &&
(!adapter->sta_info[i].
is_deauth_in_progress)) {
qdf_mem_copy(
mac,
adapter->sta_info[i].
sta_mac.bytes,
QDF_MAC_ADDR_SIZE);
hdd_debug("Delete STA with MAC::"
QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(mac));
if (hdd_ctx->dev_dfs_cac_status ==
DFS_CAC_IN_PROGRESS)
goto fn_end;
qdf_event_reset(&hapd_state->qdf_sta_disassoc_event);
qdf_status =
hdd_softap_sta_deauth(adapter,
param);
if (QDF_IS_STATUS_SUCCESS(qdf_status)) {
adapter->sta_info[i].
is_deauth_in_progress = true;
qdf_status =
qdf_wait_for_event_completion(
&hapd_state->
qdf_sta_disassoc_event,
SME_PEER_DISCONNECT_TIMEOUT);
if (!QDF_IS_STATUS_SUCCESS(
qdf_status))
hdd_warn("Deauth wait time expired");
}
}
}
} else {
qdf_status =
hdd_softap_get_sta_id(adapter,
(struct qdf_mac_addr *) mac,
&sta_id);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_debug("Skip DEL STA as this is not used::"
QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(mac));
return -ENOENT;
}
if (adapter->sta_info[sta_id].is_deauth_in_progress ==
true) {
hdd_debug("Skip DEL STA as deauth is in progress::"
QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(mac));
return -ENOENT;
}
adapter->sta_info[sta_id].is_deauth_in_progress = true;
hdd_debug("ucast, Delete STA with MAC:" QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(mac));
/* Case: SAP in ACS selected DFS ch and client connected
* Now Radar detected. Then if random channel is another
* DFS ch then new CAC is initiated and no TX allowed.
* So do not send any mgmt frames as it will timeout
* during CAC.
*/
if (hdd_ctx->dev_dfs_cac_status == DFS_CAC_IN_PROGRESS)
goto fn_end;
qdf_event_reset(&hapd_state->qdf_sta_disassoc_event);
sme_send_disassoc_req_frame(mac_handle,
adapter->vdev_id,
(uint8_t *)&param->peerMacAddr,
param->reason_code, 0);
qdf_status = hdd_softap_sta_deauth(adapter,
param);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
adapter->sta_info[sta_id].is_deauth_in_progress =
false;
hdd_debug("STA removal failed for ::"
QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(mac));
return -ENOENT;
}
qdf_status = qdf_wait_for_event_completion(
&hapd_state->
qdf_sta_disassoc_event,
SME_PEER_DISCONNECT_TIMEOUT);
if (!QDF_IS_STATUS_SUCCESS(qdf_status))
hdd_warn("Deauth wait time expired");
}
}
fn_end:
hdd_exit();
return 0;
}
#if defined(USE_CFG80211_DEL_STA_V2)
/**
* wlan_hdd_del_station() - delete station wrapper
* @adapter: pointer to the hdd adapter
*
* Return: None
*/
void wlan_hdd_del_station(struct hdd_adapter *adapter)
{
struct station_del_parameters del_sta;
del_sta.mac = NULL;
del_sta.subtype = SIR_MAC_MGMT_DEAUTH >> 4;
del_sta.reason_code = eCsrForcedDeauthSta;
wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, adapter->dev,
&del_sta);
}
#else
void wlan_hdd_del_station(struct hdd_adapter *adapter)
{
wlan_hdd_cfg80211_del_station(adapter->wdev.wiphy, adapter->dev, NULL);
}
#endif
/**
* wlan_hdd_cfg80211_del_station() - delete station entry handler
* @wiphy: Pointer to wiphy
* @dev: net_device to operate against
* @mac: binary mac address
* @reason_code: reason for the deauthorization/disassociation
* @subtype: management frame subtype to indicate removal
*
* Return: Errno
*/
static int _wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *mac,
uint16_t reason_code,
uint8_t subtype)
{
int errno;
struct csr_del_sta_params delStaParams;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
wlansap_populate_del_sta_params(mac, reason_code, subtype,
&delStaParams);
errno = __wlan_hdd_cfg80211_del_station(wiphy, dev, &delStaParams);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifdef USE_CFG80211_DEL_STA_V2
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
struct station_del_parameters *param)
{
if (!param)
return -EINVAL;
return _wlan_hdd_cfg80211_del_station(wiphy, dev, param->mac,
param->reason_code,
param->subtype);
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
const uint8_t *mac)
{
uint16_t reason = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON;
uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4;
return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype);
}
#else
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
uint8_t *mac)
{
uint16_t reason = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON;
uint8_t subtype = SIR_MAC_MGMT_DEAUTH >> 4;
return _wlan_hdd_cfg80211_del_station(wiphy, dev, mac, reason, subtype);
}
#endif
/**
* __wlan_hdd_cfg80211_add_station() - add station
* @wiphy: Pointer to wiphy
* @mac: Pointer to station mac address
* @pmksa: Pointer to add station parameter
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *mac,
struct station_parameters *params)
{
int status = -EPERM;
#ifdef FEATURE_WLAN_TDLS
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
u32 mask, set;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_STA,
adapter->vdev_id, params->listen_interval);
if (0 != wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
mask = params->sta_flags_mask;
set = params->sta_flags_set;
hdd_debug("mask 0x%x set 0x%x " QDF_MAC_ADDR_STR, mask, set,
QDF_MAC_ADDR_ARRAY(mac));
if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
struct wlan_objmgr_vdev *vdev;
vdev = hdd_objmgr_get_vdev(adapter);
if (vdev) {
status = wlan_cfg80211_tdls_add_peer(vdev,
mac);
hdd_objmgr_put_vdev(vdev);
}
}
}
#endif
hdd_exit();
return status;
}
/**
* wlan_hdd_cfg80211_add_station() - add station
* @wiphy: Pointer to wiphy
* @mac: Pointer to station mac address
* @pmksa: Pointer to add station parameter
*
* Return: 0 for success, non-zero for failure
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev,
const uint8_t *mac,
struct station_parameters *params)
#else
static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev, uint8_t *mac,
struct station_parameters *params)
#endif
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_add_station(wiphy, dev, mac, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifdef WLAN_CONV_CRYPTO_IE_SUPPORT
static QDF_STATUS wlan_hdd_set_pmksa_cache(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache)
{
QDF_STATUS result;
struct wlan_crypto_pmksa *pmksa;
struct wlan_objmgr_vdev *vdev;
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return QDF_STATUS_E_FAILURE;
pmksa = qdf_mem_malloc(sizeof(*pmksa));
if (!pmksa) {
hdd_objmgr_put_vdev(vdev);
return QDF_STATUS_E_NOMEM;
}
qdf_copy_macaddr(&pmksa->bssid, &pmk_cache->BSSID);
qdf_mem_copy(pmksa->pmkid, pmk_cache->PMKID, PMKID_LEN);
qdf_mem_copy(pmksa->pmk, pmk_cache->pmk, pmk_cache->pmk_len);
pmksa->pmk_len = pmk_cache->pmk_len;
result = wlan_crypto_set_del_pmksa(vdev, pmksa, true);
if (result != QDF_STATUS_SUCCESS) {
qdf_mem_zero(pmksa, sizeof(*pmksa));
qdf_mem_free(pmksa);
}
hdd_objmgr_put_vdev(vdev);
return result;
}
static QDF_STATUS wlan_hdd_del_pmksa_cache(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache)
{
QDF_STATUS result;
struct wlan_crypto_pmksa pmksa;
struct wlan_objmgr_vdev *vdev;
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return QDF_STATUS_E_FAILURE;
qdf_copy_macaddr(&pmksa.bssid, &pmk_cache->BSSID);
result = wlan_crypto_set_del_pmksa(adapter->vdev, &pmksa, false);
hdd_objmgr_put_vdev(vdev);
return result;
}
QDF_STATUS wlan_hdd_flush_pmksa_cache(struct hdd_adapter *adapter)
{
QDF_STATUS result;
struct wlan_objmgr_vdev *vdev;
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev)
return QDF_STATUS_E_FAILURE;
result = wlan_crypto_set_del_pmksa(adapter->vdev, NULL, false);
hdd_objmgr_put_vdev(vdev);
return result;
}
#else
static QDF_STATUS wlan_hdd_set_pmksa_cache(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
return sme_roam_set_pmkid_cache(
hdd_ctx->mac_handle, adapter->vdev_id, pmk_cache, 1, false);
}
static QDF_STATUS wlan_hdd_del_pmksa_cache(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
return sme_roam_del_pmkid_from_cache(
hdd_ctx->mac_handle, adapter->vdev_id, pmk_cache,
false);
}
QDF_STATUS wlan_hdd_flush_pmksa_cache(struct hdd_adapter *adapter)
{
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
return sme_roam_del_pmkid_from_cache(
hdd_ctx->mac_handle, adapter->vdev_id, NULL, true);
}
#endif
#if defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
/*
* wlan_hdd_is_pmksa_valid: API to validate pmksa
* @pmksa: pointer to cfg80211_pmksa structure
*
* Return: True if valid else false
*/
static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa)
{
if (!pmksa->bssid) {
hdd_warn("bssid (%pK) is NULL",
pmksa->bssid);
if (!pmksa->ssid || !pmksa->cache_id) {
hdd_err("either ssid (%pK) or cache_id (%pK) are NULL",
pmksa->ssid, pmksa->cache_id);
return false;
}
}
return true;
}
/*
* hdd_fill_pmksa_info: API to update tPmkidCacheInfo from cfg80211_pmksa
* @adapter: Pointer to hdd adapter
* @pmk_cache: pmk that needs to be udated
* @pmksa: pmk from supplicant
* @is_delete: Bool to decide set or delete PMK
* Return: None
*/
static void hdd_fill_pmksa_info(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache,
struct cfg80211_pmksa *pmksa, bool is_delete)
{
if (pmksa->bssid) {
hdd_debug("%s PMKSA for " QDF_MAC_ADDR_STR,
is_delete ? "Delete" : "Set",
QDF_MAC_ADDR_ARRAY(pmksa->bssid));
qdf_mem_copy(pmk_cache->BSSID.bytes,
pmksa->bssid, QDF_MAC_ADDR_SIZE);
} else {
qdf_mem_copy(pmk_cache->ssid, pmksa->ssid, pmksa->ssid_len);
qdf_mem_copy(pmk_cache->cache_id, pmksa->cache_id,
CACHE_ID_LEN);
pmk_cache->ssid_len = pmksa->ssid_len;
hdd_debug("%s PMKSA for ssid %*.*s cache_id %x %x",
is_delete ? "Delete" : "Set",
pmk_cache->ssid_len, pmk_cache->ssid_len,
pmk_cache->ssid, pmk_cache->cache_id[0],
pmk_cache->cache_id[1]);
}
if (is_delete)
return;
qdf_mem_copy(pmk_cache->PMKID, pmksa->pmkid, PMKID_LEN);
if (pmksa->pmk_len && (pmksa->pmk_len <= CSR_RSN_MAX_PMK_LEN)) {
qdf_mem_copy(pmk_cache->pmk, pmksa->pmk, pmksa->pmk_len);
pmk_cache->pmk_len = pmksa->pmk_len;
} else
hdd_debug("pmk len is %zu", pmksa->pmk_len);
}
#else
/*
* wlan_hdd_is_pmksa_valid: API to validate pmksa
* @pmksa: pointer to cfg80211_pmksa structure
*
* Return: True if valid else false
*/
static inline bool wlan_hdd_is_pmksa_valid(struct cfg80211_pmksa *pmksa)
{
if (!pmksa->bssid) {
hdd_err("both bssid is NULL %pK", pmksa->bssid);
return false;
}
return true;
}
/*
* hdd_fill_pmksa_info: API to update tPmkidCacheInfo from cfg80211_pmksa
* @adapter: Pointer to hdd adapter
* @pmk_cache: pmk which needs to be updated
* @pmksa: pmk from supplicant
* @is_delete: Bool to decide whether to set or delete PMK
*
* Return: None
*/
static void hdd_fill_pmksa_info(struct hdd_adapter *adapter,
tPmkidCacheInfo *pmk_cache,
struct cfg80211_pmksa *pmksa, bool is_delete)
{
mac_handle_t mac_handle;
hdd_debug("%s PMKSA for " QDF_MAC_ADDR_STR, is_delete ? "Delete" : "Set",
QDF_MAC_ADDR_ARRAY(pmksa->bssid));
qdf_mem_copy(pmk_cache->BSSID.bytes,
pmksa->bssid, QDF_MAC_ADDR_SIZE);
if (is_delete)
return;
mac_handle = hdd_adapter_get_mac_handle(adapter);
sme_get_pmk_info(mac_handle, adapter->vdev_id, pmk_cache);
qdf_mem_copy(pmk_cache->PMKID, pmksa->pmkid, PMKID_LEN);
}
#endif
/**
* __wlan_hdd_cfg80211_set_pmksa() - set pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @pmksa: Pointer to set pmksa parameter
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
mac_handle_t mac_handle;
QDF_STATUS result = QDF_STATUS_SUCCESS;
int status;
tPmkidCacheInfo pmk_cache;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
if (!pmksa) {
hdd_err("pmksa is NULL");
return -EINVAL;
}
if (!pmksa->pmkid) {
hdd_err("pmksa->pmkid(%pK) is NULL",
pmksa->pmkid);
return -EINVAL;
}
if (!wlan_hdd_is_pmksa_valid(pmksa))
return -EINVAL;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
mac_handle = hdd_ctx->mac_handle;
qdf_mem_zero(&pmk_cache, sizeof(pmk_cache));
hdd_fill_pmksa_info(adapter, &pmk_cache, pmksa, false);
/*
* Add to the PMKSA Cache in CSR
* PMKSA cache will be having following
* 1. pmkid id
* 2. pmk
* 3. bssid or cache identifier
*/
result = wlan_hdd_set_pmksa_cache(adapter, &pmk_cache);
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_PMKSA,
adapter->vdev_id, result);
sme_set_del_pmkid_cache(mac_handle, adapter->vdev_id,
&pmk_cache, true);
qdf_mem_zero(&pmk_cache, sizeof(pmk_cache));
hdd_exit();
return QDF_IS_STATUS_SUCCESS(result) ? 0 : -EINVAL;
}
/**
* wlan_hdd_cfg80211_set_pmksa() - set pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @pmksa: Pointer to set pmksa parameter
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_pmksa(wiphy, dev, pmksa);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_del_pmksa() - delete pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @pmksa: Pointer to pmksa parameter
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
mac_handle_t mac_handle;
int status = 0;
tPmkidCacheInfo pmk_cache;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
if (!pmksa) {
hdd_err("pmksa is NULL");
return -EINVAL;
}
if (!wlan_hdd_is_pmksa_valid(pmksa))
return -EINVAL;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
mac_handle = hdd_ctx->mac_handle;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DEL_PMKSA,
adapter->vdev_id, 0);
qdf_mem_zero(&pmk_cache, sizeof(pmk_cache));
hdd_fill_pmksa_info(adapter, &pmk_cache, pmksa, true);
/* Delete the PMKID CSR cache */
if (QDF_STATUS_SUCCESS !=
wlan_hdd_del_pmksa_cache(adapter, &pmk_cache)) {
hdd_err("Failed to delete PMKSA for " QDF_MAC_ADDR_STR,
QDF_MAC_ADDR_ARRAY(pmksa->bssid));
status = -EINVAL;
}
sme_set_del_pmkid_cache(mac_handle, adapter->vdev_id, &pmk_cache,
false);
qdf_mem_zero(&pmk_cache, sizeof(pmk_cache));
hdd_exit();
return status;
}
/**
* wlan_hdd_cfg80211_del_pmksa() - delete pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @pmksa: Pointer to pmksa parameter
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_del_pmksa(wiphy, dev, pmksa);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
/**
* __wlan_hdd_cfg80211_flush_pmksa() - flush pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy,
struct net_device *dev)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
mac_handle_t mac_handle;
int errno;
QDF_STATUS status;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
hdd_debug("Flushing PMKSA");
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
mac_handle = hdd_ctx->mac_handle;
status = wlan_hdd_flush_pmksa_cache(adapter);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Cannot flush PMKIDCache");
errno = -EINVAL;
}
sme_set_del_pmkid_cache(mac_handle, adapter->vdev_id, NULL, false);
hdd_exit();
return errno;
}
/**
* wlan_hdd_cfg80211_flush_pmksa() - flush pmksa
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy,
struct net_device *dev)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_flush_pmksa(wiphy, dev);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#if defined(KERNEL_SUPPORT_11R_CFG80211)
/**
* __wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @ftie: Pointer to fast transition ie parameter
*
* Return: 0 for success, non-zero for failure
*/
static int
__wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
int status;
mac_handle_t mac_handle;
hdd_enter();
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES,
adapter->vdev_id, sta_ctx->conn_info.conn_state);
/* Added for debug on reception of Re-assoc Req. */
if (eConnectionState_Associated != sta_ctx->conn_info.conn_state) {
hdd_err("Called with Ie of length = %zu when not associated",
ftie->ie_len);
hdd_err("Should be Re-assoc Req IEs");
}
hdd_debug("%s called with Ie of length = %zu", __func__,
ftie->ie_len);
/* Pass the received FT IEs to SME */
mac_handle = hdd_ctx->mac_handle;
sme_set_ft_ies(mac_handle, adapter->vdev_id,
(const u8 *)ftie->ie, ftie->ie_len);
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_update_ft_ies() - update fast transition ies
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @ftie: Pointer to fast transition ie parameter
*
* Return: 0 for success, non-zero for failure
*/
static int
wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_update_ft_ies(wiphy, dev, ftie);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT
/**
* __wlan_hdd_cfg80211_update_owe_info() - update OWE info
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @owe_info: Pointer to OWE info
*
* Return: 0 for success, non-zero for failure
*/
static int
__wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_update_owe_info *owe_info)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
QDF_STATUS status;
int errno;
hdd_enter_dev(dev);
errno = wlan_hdd_validate_context(hdd_ctx);
if (errno)
return errno;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) {
hdd_err("invalid vdev id: %d", adapter->vdev_id);
return -EINVAL;
}
hdd_debug("owe_status %d", owe_info->status);
status = wlansap_update_owe_info(WLAN_HDD_GET_SAP_CTX_PTR(adapter),
owe_info->peer, owe_info->ie,
owe_info->ie_len, owe_info->status);
if (QDF_IS_STATUS_ERROR(status)) {
hdd_err("Failed to update OWE info");
errno = qdf_status_to_os_return(status);
}
hdd_exit();
return errno;
}
/**
* wlan_hdd_cfg80211_update_owe_info() - update OWE info
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @owe_info: Pointer to OWE info
*
* Return: 0 for success, non-zero for failure
*/
static int
wlan_hdd_cfg80211_update_owe_info(struct wiphy *wiphy,
struct net_device *net_dev,
struct cfg80211_update_owe_info *owe_info)
{
struct osif_vdev_sync *vdev_sync;
int errno;
errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_update_owe_info(wiphy, net_dev, owe_info);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
void wlan_hdd_cfg80211_update_replay_counter_cb(
void *cb_ctx, struct pmo_gtk_rsp_params *gtk_rsp_param)
{
struct hdd_adapter *adapter = (struct hdd_adapter *)cb_ctx;
uint8_t temp_replay_counter[8];
int i;
uint8_t *p;
hdd_enter();
if (!adapter) {
hdd_err("HDD adapter is Null");
goto out;
}
if (!gtk_rsp_param) {
hdd_err("gtk_rsp_param is Null");
goto out;
}
if (gtk_rsp_param->status_flag != QDF_STATUS_SUCCESS) {
hdd_err("wlan Failed to get replay counter value");
goto out;
}
hdd_debug("updated replay counter: %llu from fwr",
gtk_rsp_param->replay_counter);
/* convert little to big endian since supplicant works on big endian */
p = (uint8_t *)&gtk_rsp_param->replay_counter;
for (i = 0; i < 8; i++)
temp_replay_counter[7 - i] = (uint8_t) p[i];
hdd_debug("gtk_rsp_param bssid %pM", gtk_rsp_param->bssid.bytes);
/* Update replay counter to NL */
cfg80211_gtk_rekey_notify(adapter->dev,
gtk_rsp_param->bssid.bytes,
temp_replay_counter, GFP_KERNEL);
out:
hdd_exit();
}
#ifdef WLAN_FEATURE_GTK_OFFLOAD
/**
* wlan_hdd_copy_gtk_kek - Copy the KEK from GTK rekey data to GTK request
* @gtk_req: Pointer to GTK request
* @data: Pointer to rekey data
*
* Return: none
*/
#ifdef CFG80211_REKEY_DATA_KEK_LEN
static
void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req,
struct cfg80211_gtk_rekey_data *data)
{
qdf_mem_copy(gtk_req->kek, data->kek, data->kek_len);
gtk_req->kek_len = data->kek_len;
}
#else
static
void wlan_hdd_copy_gtk_kek(struct pmo_gtk_req *gtk_req,
struct cfg80211_gtk_rekey_data *data)
{
qdf_mem_copy(gtk_req->kek, data->kek, NL80211_KEK_LEN);
gtk_req->kek_len = NL80211_KEK_LEN;
}
#endif
/**
* __wlan_hdd_cfg80211_set_rekey_data() - set rekey data
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @data: Pointer to rekey data
*
* This function is used to offload GTK rekeying job to the firmware.
*
* Return: 0 for success, non-zero for failure
*/
static
int __wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_gtk_rekey_data *data)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
int result, i;
struct pmo_gtk_req *gtk_req = NULL;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint8_t *buf;
struct wlan_objmgr_vdev *vdev;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
result = -EINVAL;
goto out;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id)) {
result = -EINVAL;
goto out;
}
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA,
adapter->vdev_id, adapter->device_mode);
result = wlan_hdd_validate_context(hdd_ctx);
if (0 != result)
goto out;
gtk_req = qdf_mem_malloc(sizeof(*gtk_req));
if (!gtk_req) {
result = -ENOMEM;
goto out;
}
/* convert big to little endian since driver work on little endian */
buf = (uint8_t *)&gtk_req->replay_counter;
for (i = 0; i < 8; i++)
buf[7 - i] = data->replay_ctr[i];
hdd_debug("current replay counter: %llu in user space",
gtk_req->replay_counter);
wlan_hdd_copy_gtk_kek(gtk_req, data);
if (data->kck)
qdf_mem_copy(gtk_req->kck, data->kck, NL80211_KCK_LEN);
gtk_req->is_fils_connection = hdd_is_fils_connection(adapter);
vdev = hdd_objmgr_get_vdev(adapter);
if (!vdev) {
result = -EINVAL;
goto out;
}
status = ucfg_pmo_cache_gtk_offload_req(vdev, gtk_req);
hdd_objmgr_put_vdev(vdev);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Failed to cache GTK Offload");
result = qdf_status_to_os_return(status);
}
out:
if (gtk_req)
qdf_mem_free(gtk_req);
hdd_exit();
return result;
}
/**
* wlan_hdd_cfg80211_set_rekey_data() - set rekey data
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @data: Pointer to rekey data
*
* This function is used to offload GTK rekeying job to the firmware.
*
* Return: 0 for success, non-zero for failure
*/
static
int wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_gtk_rekey_data *data)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_rekey_data(wiphy, dev, data);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif /* WLAN_FEATURE_GTK_OFFLOAD */
/**
* __wlan_hdd_cfg80211_set_mac_acl() - set access control policy
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @param: Pointer to access control parameter
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy,
struct net_device *dev,
const struct cfg80211_acl_data *params)
{
int i;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_hostapd_state *hostapd_state;
struct sap_config *config;
struct hdd_context *hdd_ctx;
int status;
QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
hdd_enter();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (!params) {
hdd_err("params is Null");
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return status;
hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(adapter);
if (!hostapd_state) {
hdd_err("hostapd_state is Null");
return -EINVAL;
}
hdd_debug("acl policy: %d num acl entries: %d", params->acl_policy,
params->n_acl_entries);
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_MAC_ACL,
adapter->vdev_id, adapter->device_mode);
if (QDF_SAP_MODE == adapter->device_mode) {
config = &adapter->session.ap.sap_config;
/* default value */
config->num_accept_mac = 0;
config->num_deny_mac = 0;
/**
* access control policy
* @NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: Deny stations which are
* listed in hostapd.deny file.
* @NL80211_ACL_POLICY_DENY_UNLESS_LISTED: Allow stations which are
* listed in hostapd.accept file.
*/
if (NL80211_ACL_POLICY_DENY_UNLESS_LISTED == params->acl_policy) {
config->SapMacaddr_acl = eSAP_DENY_UNLESS_ACCEPTED;
} else if (NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED ==
params->acl_policy) {
config->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED;
} else {
hdd_warn("Acl Policy : %d is not supported",
params->acl_policy);
return -ENOTSUPP;
}
if (eSAP_DENY_UNLESS_ACCEPTED == config->SapMacaddr_acl) {
config->num_accept_mac = params->n_acl_entries;
for (i = 0; i < params->n_acl_entries; i++) {
hdd_debug("** Add ACL MAC entry %i in WhiletList :"
QDF_MAC_ADDR_STR, i,
QDF_MAC_ADDR_ARRAY(
params->mac_addrs[i].addr));
qdf_mem_copy(&config->accept_mac[i],
params->mac_addrs[i].addr,
QDF_MAC_ADDR_SIZE);
}
} else if (eSAP_ACCEPT_UNLESS_DENIED == config->SapMacaddr_acl) {
config->num_deny_mac = params->n_acl_entries;
for (i = 0; i < params->n_acl_entries; i++) {
hdd_debug("** Add ACL MAC entry %i in BlackList :"
QDF_MAC_ADDR_STR, i,
QDF_MAC_ADDR_ARRAY(
params->mac_addrs[i].addr));
qdf_mem_copy(&config->deny_mac[i],
params->mac_addrs[i].addr,
QDF_MAC_ADDR_SIZE);
}
}
qdf_status = wlansap_set_mac_acl(
WLAN_HDD_GET_SAP_CTX_PTR(adapter), config);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
hdd_err("SAP Set Mac Acl fail");
return -EINVAL;
}
} else {
hdd_debug("Invalid device_mode %s(%d)",
qdf_opmode_str(adapter->device_mode),
adapter->device_mode);
return -EINVAL;
}
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_set_mac_acl() - SSR wrapper for
* __wlan_hdd_cfg80211_set_mac_acl
* @wiphy: pointer to wiphy structure
* @dev: pointer to net_device
* @params: pointer to cfg80211_acl_data
*
* Return; 0 on success, error number otherwise
*/
static int
wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy,
struct net_device *dev,
const struct cfg80211_acl_data *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_mac_acl(wiphy, dev, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#ifdef WLAN_NL80211_TESTMODE
#ifdef FEATURE_WLAN_LPHB
/**
* wlan_hdd_cfg80211_lphb_ind_handler() - handle low power heart beat indication
* @hdd_ctx: Pointer to hdd context
* @lphbInd: Pointer to low power heart beat indication parameter
*
* Return: none
*/
static void wlan_hdd_cfg80211_lphb_ind_handler(void *hdd_ctx,
struct pmo_lphb_rsp *lphb_ind)
{
struct sk_buff *skb;
hdd_debug("LPHB indication arrived");
if (0 != wlan_hdd_validate_context((struct hdd_context *) hdd_ctx))
return;
if (!lphb_ind) {
hdd_err("invalid argument lphbInd");
return;
}
skb = cfg80211_testmode_alloc_event_skb(((struct hdd_context *) hdd_ctx)->
wiphy, sizeof(*lphb_ind), GFP_ATOMIC);
if (!skb) {
hdd_err("LPHB timeout, NL buffer alloc fail");
return;
}
if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_CMD, WLAN_HDD_TM_CMD_WLAN_HB)) {
hdd_err("WLAN_HDD_TM_ATTR_CMD put fail");
goto nla_put_failure;
}
if (nla_put_u32(skb, WLAN_HDD_TM_ATTR_TYPE, lphb_ind->protocol_type)) {
hdd_err("WLAN_HDD_TM_ATTR_TYPE put fail");
goto nla_put_failure;
}
if (nla_put(skb, WLAN_HDD_TM_ATTR_DATA, sizeof(*lphb_ind),
lphb_ind)) {
hdd_err("WLAN_HDD_TM_ATTR_DATA put fail");
goto nla_put_failure;
}
cfg80211_testmode_event(skb, GFP_ATOMIC);
return;
nla_put_failure:
hdd_err("NLA Put fail");
kfree_skb(skb);
}
#endif /* FEATURE_WLAN_LPHB */
/**
* __wlan_hdd_cfg80211_testmode() - test mode
* @wiphy: Pointer to wiphy
* @data: Data pointer
* @len: Data length
*
* Return: 0 for success, non-zero for failure
*/
static int __wlan_hdd_cfg80211_testmode(struct wiphy *wiphy,
void *data, int len)
{
struct nlattr *tb[WLAN_HDD_TM_ATTR_MAX + 1];
int err;
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
hdd_enter();
err = wlan_hdd_validate_context(hdd_ctx);
if (err)
return err;
if (hdd_ctx->driver_status == DRIVER_MODULES_CLOSED) {
hdd_err("Driver Modules are closed");
return -EINVAL;
}
err = wlan_cfg80211_nla_parse(tb, WLAN_HDD_TM_ATTR_MAX, data,
len, wlan_hdd_tm_policy);
if (err) {
hdd_err("Testmode INV ATTR");
return err;
}
if (!tb[WLAN_HDD_TM_ATTR_CMD]) {
hdd_err("Testmode INV CMD");
return -EINVAL;
}
qdf_mtrace(QDF_MODULE_ID_HDD, QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_TESTMODE,
NO_SESSION, nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD]));
switch (nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD])) {
#ifdef FEATURE_WLAN_LPHB
/* Low Power Heartbeat configuration request */
case WLAN_HDD_TM_CMD_WLAN_HB:
{
int buf_len;
void *buf;
struct pmo_lphb_req *hb_params = NULL;
struct pmo_lphb_req *hb_params_temp = NULL;
QDF_STATUS status;
if (!tb[WLAN_HDD_TM_ATTR_DATA]) {
hdd_err("Testmode INV DATA");
return -EINVAL;
}
buf = nla_data(tb[WLAN_HDD_TM_ATTR_DATA]);
buf_len = nla_len(tb[WLAN_HDD_TM_ATTR_DATA]);
if (buf_len < sizeof(*hb_params_temp)) {
hdd_err("Invalid buffer length for TM_ATTR_DATA");
return -EINVAL;
}
hb_params_temp = (struct pmo_lphb_req *) buf;
if ((hb_params_temp->cmd == pmo_lphb_set_tcp_pararm_indid)
&& (hb_params_temp->params.lphb_tcp_params.
time_period_sec == 0))
return -EINVAL;
if (buf_len > sizeof(*hb_params)) {
hdd_err("buf_len=%d exceeded hb_params size limit",
buf_len);
return -ERANGE;
}
hb_params = (struct pmo_lphb_req *)qdf_mem_malloc(
sizeof(*hb_params));
if (!hb_params)
return -ENOMEM;
qdf_mem_zero(hb_params, sizeof(*hb_params));
qdf_mem_copy(hb_params, buf, buf_len);
status = ucfg_pmo_lphb_config_req(
hdd_ctx->psoc,
hb_params, (void *)hdd_ctx,
wlan_hdd_cfg80211_lphb_ind_handler);
if (status != QDF_STATUS_SUCCESS)
hdd_err("LPHB Config Fail, disable");
qdf_mem_free(hb_params);
return 0;
}
#endif /* FEATURE_WLAN_LPHB */
#if defined(QCA_WIFI_FTM)
case WLAN_HDD_TM_CMD_WLAN_FTM:
{
if (QDF_GLOBAL_FTM_MODE != hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode, mode %d",
hdd_get_conparam());
return -EINVAL;
}
err = wlan_cfg80211_ftm_testmode_cmd(hdd_ctx->pdev,
data, len);
break;
}
#endif
default:
hdd_err("command: %d not supported",
nla_get_u32(tb[WLAN_HDD_TM_ATTR_CMD]));
return -EOPNOTSUPP;
}
hdd_exit();
return err;
}
/**
* wlan_hdd_cfg80211_testmode() - test mode
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @data: Data pointer
* @len: Data length
*
* Return: 0 for success, non-zero for failure
*/
static int wlan_hdd_cfg80211_testmode(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
struct wireless_dev *wdev,
#endif
void *data, int len)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_testmode(wiphy, data, len);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#endif /* CONFIG_NL80211_TESTMODE */
#ifdef QCA_HT_2040_COEX
/**
* __wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @chandef: Pointer to channel definition parameter
*
* Return: 0 for success, non-zero for failure
*/
static int
__wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx;
QDF_STATUS status;
int retval = 0;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
if (!(adapter->device_mode == QDF_SAP_MODE ||
adapter->device_mode == QDF_P2P_GO_MODE))
return -EOPNOTSUPP;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return status;
hdd_debug("Channel width changed to %d ",
cfg80211_get_chandef_type(chandef));
/* Change SAP ht2040 mode */
status = hdd_set_sap_ht2040_mode(adapter,
cfg80211_get_chandef_type(chandef));
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Cannot set SAP HT20/40 mode!");
retval = -EINVAL;
}
return retval;
}
/**
* wlan_hdd_cfg80211_set_ap_channel_width() - set ap channel bandwidth
* @wiphy: Pointer to wiphy
* @dev: Pointer to network device
* @chandef: Pointer to channel definition parameter
*
* Return: 0 for success, non-zero for failure
*/
static int
wlan_hdd_cfg80211_set_ap_channel_width(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_chan_def *chandef)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_ap_channel_width(wiphy, dev, chandef);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
#ifdef CHANNEL_SWITCH_SUPPORTED
/**
* __wlan_hdd_cfg80211_channel_switch()- function to switch
* channel in SAP/GO
* @wiphy: wiphy pointer
* @dev: dev pointer.
* @csa_params: Change channel params
*
* This function is called to switch channel in SAP/GO
*
* Return: 0 if success else return non zero
*/
static int __wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_csa_settings *csa_params)
{
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx;
uint8_t channel;
uint16_t freq;
int ret;
enum phy_ch_width ch_width;
hdd_debug("Set Freq %d",
csa_params->chandef.chan->center_freq);
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if ((QDF_P2P_GO_MODE != adapter->device_mode) &&
(QDF_SAP_MODE != adapter->device_mode))
return -ENOTSUPP;
freq = csa_params->chandef.chan->center_freq;
channel = cds_freq_to_chan(freq);
ch_width = hdd_map_nl_chan_width(csa_params->chandef.width);
ret = hdd_softap_set_channel_change(dev, channel, ch_width, false);
return ret;
}
/**
* wlan_hdd_cfg80211_channel_switch()- function to switch
* channel in SAP/GO
* @wiphy: wiphy pointer
* @dev: dev pointer.
* @csa_params: Change channel params
*
* This function is called to switch channel in SAP/GO
*
* Return: 0 if success else return non zero
*/
static int wlan_hdd_cfg80211_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_csa_settings *csa_params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_channel_switch(wiphy, dev, csa_params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
int wlan_hdd_change_hw_mode_for_given_chnl(struct hdd_adapter *adapter,
uint8_t channel,
enum policy_mgr_conn_update_reason reason)
{
QDF_STATUS status;
struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_enter();
if (0 != wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
status = policy_mgr_reset_connection_update(hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("clearing event failed");
status = policy_mgr_current_connections_update(hdd_ctx->psoc,
adapter->vdev_id, channel, reason);
switch (status) {
case QDF_STATUS_E_FAILURE:
/*
* QDF_STATUS_E_FAILURE indicates that some error has occurred
* while changing the hw mode
*/
hdd_err("ERROR: connections update failed!!");
return -EINVAL;
case QDF_STATUS_SUCCESS:
/*
* QDF_STATUS_SUCCESS indicates that HW mode change has been
* triggered and wait for it to finish.
*/
status = policy_mgr_wait_for_connection_update(
hdd_ctx->psoc);
if (!QDF_IS_STATUS_SUCCESS(status)) {
hdd_err("ERROR: qdf wait for event failed!!");
return -EINVAL;
}
if (QDF_MONITOR_MODE == adapter->device_mode)
hdd_info("Monitor mode:channel:%d (SMM->DBS)", channel);
break;
default:
/*
* QDF_STATUS_E_NOSUPPORT indicates that no HW mode change is
* required, so caller can proceed further.
*/
break;
}
hdd_exit();
return 0;
}
#ifdef FEATURE_MONITOR_MODE_SUPPORT
/**
* wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel
* @wiphy: Handle to struct wiphy to get handle to module context.
* @chandef: Contains information about the capture channel to be set.
*
* This interface is called if and only if monitor mode interface alone is
* active.
*
* Return: 0 success or error code on failure.
*/
static int __wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct hdd_adapter *adapter;
struct hdd_station_ctx *sta_ctx;
struct hdd_mon_set_ch_info *ch_info;
QDF_STATUS status;
mac_handle_t mac_handle;
struct qdf_mac_addr bssid;
struct csr_roam_profile roam_profile;
struct ch_params ch_params;
uint8_t sec_ch = 0;
int ret;
uint16_t chan_num = cds_freq_to_chan(chandef->chan->center_freq);
hdd_enter();
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
mac_handle = hdd_ctx->mac_handle;
adapter = hdd_get_adapter(hdd_ctx, QDF_MONITOR_MODE);
if (!adapter)
return -EIO;
hdd_debug("%s: set monitor mode Channel %d and freq %d",
adapter->dev->name, chan_num, chandef->chan->center_freq);
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
ch_info = &sta_ctx->ch_info;
roam_profile.ChannelInfo.ChannelList = &ch_info->channel;
roam_profile.ChannelInfo.numOfChannels = 1;
roam_profile.phyMode = ch_info->phy_mode;
roam_profile.ch_params.ch_width = hdd_map_nl_chan_width(chandef->width);
hdd_select_cbmode(adapter, chan_num, &roam_profile.ch_params);
qdf_mem_copy(bssid.bytes, adapter->mac_addr.bytes,
QDF_MAC_ADDR_SIZE);
ch_params.ch_width = hdd_map_nl_chan_width(chandef->width);
/*
* CDS api expects secondary channel for calculating
* the channel params
*/
if ((ch_params.ch_width == CH_WIDTH_40MHZ) &&
(WLAN_REG_IS_24GHZ_CH(chan_num))) {
if (chan_num >= 1 && chan_num <= 5)
sec_ch = chan_num + 4;
else if (chan_num >= 6 && chan_num <= 13)
sec_ch = chan_num - 4;
}
wlan_reg_set_channel_params(hdd_ctx->pdev, chan_num,
sec_ch, &ch_params);
if (wlan_hdd_change_hw_mode_for_given_chnl(adapter, chan_num,
POLICY_MGR_UPDATE_REASON_SET_OPER_CHAN)) {
hdd_err("Failed to change hw mode");
return -EINVAL;
}
status = sme_roam_channel_change_req(mac_handle, bssid, &ch_params,
&roam_profile);
if (status) {
hdd_err("Failed to set sme_RoamChannel for monitor mode status: %d",
status);
ret = qdf_status_to_os_return(status);
return ret;
}
hdd_exit();
return 0;
}
/**
* wlan_hdd_cfg80211_set_mon_ch() - Set monitor mode capture channel
* @wiphy: Handle to struct wiphy to get handle to module context.
* @chandef: Contains information about the capture channel to be set.
*
* This interface is called if and only if monitor mode interface alone is
* active.
*
* Return: 0 success or error code on failure.
*/
static int wlan_hdd_cfg80211_set_mon_ch(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
struct osif_psoc_sync *psoc_sync;
int errno;
errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_set_mon_ch(wiphy, chandef);
osif_psoc_sync_op_stop(psoc_sync);
return errno;
}
#endif
#define CNT_DIFF(cur, prev) \
((cur >= prev) ? (cur - prev) : (cur + (MAX_COUNT - (prev) + 1)))
#define MAX_COUNT 0xffffffff
static void hdd_update_chan_info(struct hdd_context *hdd_ctx,
struct scan_chan_info *chan,
struct scan_chan_info *info, uint32_t cmd_flag)
{
if ((info->cmd_flag != WMI_CHAN_InFO_START_RESP) &&
(info->cmd_flag != WMI_CHAN_InFO_END_RESP))
hdd_err("cmd flag is invalid: %d", info->cmd_flag);
mutex_lock(&hdd_ctx->chan_info_lock);
if (info->cmd_flag == WMI_CHAN_InFO_START_RESP)
qdf_mem_zero(chan, sizeof(*chan));
chan->freq = info->freq;
chan->noise_floor = info->noise_floor;
chan->clock_freq = info->clock_freq;
chan->cmd_flag = info->cmd_flag;
chan->cycle_count = CNT_DIFF(info->cycle_count, chan->cycle_count);
chan->rx_clear_count =
CNT_DIFF(info->rx_clear_count, chan->rx_clear_count);
chan->tx_frame_count =
CNT_DIFF(info->tx_frame_count, chan->tx_frame_count);
mutex_unlock(&hdd_ctx->chan_info_lock);
}
#undef CNT_DIFF
#undef MAX_COUNT
#ifndef UPDATE_ASSOC_IE
#define UPDATE_ASSOC_IE BIT(0)
#endif
#ifndef UPDATE_FILS_ERP_INFO
#define UPDATE_FILS_ERP_INFO BIT(1)
#endif
#ifndef UPDATE_FILS_AUTH_TYPE
#define UPDATE_FILS_AUTH_TYPE BIT(2)
#endif
#if defined(WLAN_FEATURE_FILS_SK) &&\
defined(CFG80211_FILS_SK_OFFLOAD_SUPPORT) &&\
(defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)))
/**
* hdd_update_connect_params_fils_info() - Update fils parameters based on
* the update_connect_params received from userspace
* @adapter: Pointer to hdd_adapter
* @hdd_ctx: Pointer to hdd_context
* @req: Pointer to connect params
* @changed: bitmap indicating which parameters have changed
*
* Return 0 on SUCCESS or error code on FAILURE
*/
static int
hdd_update_connect_params_fils_info(struct hdd_adapter *adapter,
struct hdd_context *hdd_ctx,
struct cfg80211_connect_params *req,
uint32_t changed)
{
uint8_t *buf;
QDF_STATUS status;
mac_handle_t mac_handle;
struct csr_roam_profile *roam_profile;
struct cds_fils_connection_info *fils_info;
enum eAniAuthType auth_type;
roam_profile = hdd_roam_profile(adapter);
fils_info = roam_profile->fils_con_info;
mac_handle = hdd_ctx->mac_handle;
if (!fils_info) {
hdd_err("No valid FILS conn info");
return -EINVAL;
}
fils_info->is_fils_connection = true;
if (changed & UPDATE_FILS_ERP_INFO) {
if (!wlan_hdd_fils_data_in_limits(req))
return -EINVAL;
fils_info->key_nai_length = req->fils_erp_username_len +
sizeof(char) +
req->fils_erp_realm_len;
if (fils_info->key_nai_length >
FILS_MAX_KEYNAME_NAI_LENGTH) {
hdd_err("Key NAI Length %d",
fils_info->key_nai_length);
return -EINVAL;
}
if (req->fils_erp_username_len && req->fils_erp_username) {
buf = fils_info->keyname_nai;
qdf_mem_copy(buf, req->fils_erp_username,
req->fils_erp_username_len);
buf += req->fils_erp_username_len;
*buf++ = '@';
qdf_mem_copy(buf, req->fils_erp_realm,
req->fils_erp_realm_len);
}
fils_info->sequence_number = req->fils_erp_next_seq_num + 1;
fils_info->r_rk_length = req->fils_erp_rrk_len;
if (req->fils_erp_rrk_len && req->fils_erp_rrk)
qdf_mem_copy(fils_info->r_rk, req->fils_erp_rrk,
fils_info->r_rk_length);
fils_info->realm_len = req->fils_erp_realm_len;
if (req->fils_erp_realm_len && req->fils_erp_realm)
qdf_mem_copy(fils_info->realm, req->fils_erp_realm,
fils_info->realm_len);
}
if (changed & UPDATE_FILS_AUTH_TYPE) {
auth_type = wlan_hdd_get_fils_auth_type(req->auth_type);
if (auth_type == eSIR_DONOT_USE_AUTH_TYPE) {
hdd_err("invalid auth type for fils %d",
req->auth_type);
return -EINVAL;
}
roam_profile->fils_con_info->auth_type = auth_type;
}
hdd_debug("fils conn update: changed %x is_fils %d keyname nai len %d",
changed, roam_profile->fils_con_info->is_fils_connection,
roam_profile->fils_con_info->key_nai_length);
/*
* Update the FILS config from adapter->roam_profile to
* csr_session
*/
status = sme_update_fils_config(mac_handle, adapter->vdev_id,
roam_profile);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Update FILS connect params to csr failed %d", status);
return 0;
}
#else
static inline int
hdd_update_connect_params_fils_info(struct hdd_adapter *adapter,
struct hdd_context *hdd_ctx,
struct cfg80211_connect_params *req,
uint32_t changed)
{
return -EINVAL;
}
#endif
#if defined(CFG80211_UPDATE_CONNECT_PARAMS) ||\
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
/**
* __wlan_hdd_cfg80211_update_connect_params - update connect params
* @wiphy: Handle to struct wiphy to get handle to module context.
* @dev: Pointer to network device
* @req: Pointer to connect params
* @changed: Bitmap used to indicate the changed params
*
* Update the connect parameters while connected to a BSS. The updated
* parameters can be used by driver/firmware for subsequent BSS selection
* (roaming) decisions and to form the Authentication/(Re)Association
* Request frames. This call does not request an immediate disassociation
* or reassociation with the current BSS, i.e., this impacts only
* subsequent (re)associations. The bits in changed are defined in enum
* cfg80211_connect_params_changed
*
* Return: zero for success, non-zero for failure
*/
static int
__wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_connect_params *req,
uint32_t changed)
{
struct csr_roam_profile *roam_profile;
int ret;
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
QDF_STATUS status;
mac_handle_t mac_handle;
hdd_enter_dev(dev);
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return -EINVAL;
mac_handle = hdd_ctx->mac_handle;
roam_profile = hdd_roam_profile(adapter);
if (changed & UPDATE_ASSOC_IE) {
/*
* Validate the elements of the IE and copy it to
* roam_profile in adapter
*/
wlan_hdd_cfg80211_set_ie(adapter, req->ie, req->ie_len);
/*
* Update this assoc IE received from user space to
* csr_session. RSO command will pick up the assoc
* IEs to be sent to firmware from the csr_session.
*/
sme_update_session_assoc_ie(mac_handle, adapter->vdev_id,
roam_profile);
}
if ((changed & UPDATE_FILS_ERP_INFO) ||
(changed & UPDATE_FILS_AUTH_TYPE)) {
ret = hdd_update_connect_params_fils_info(adapter, hdd_ctx,
req, changed);
if (ret)
return -EINVAL;
if (!hdd_ctx->is_fils_roaming_supported) {
hdd_debug("FILS roaming support %d",
hdd_ctx->is_fils_roaming_supported);
return 0;
}
}
if (changed) {
status = sme_send_rso_connect_params(mac_handle,
adapter->vdev_id,
roam_profile);
if (QDF_IS_STATUS_ERROR(status))
hdd_err("Update connect params to fw failed %d",
status);
}
return 0;
}
/**
* wlan_hdd_cfg80211_update_connect_params - SSR wrapper for
* __wlan_hdd_cfg80211_update_connect_params
* @wiphy: Pointer to wiphy structure
* @dev: Pointer to net_device
* @req: Pointer to connect params
* @changed: flags used to indicate the changed params
*
* Return: zero for success, non-zero for failure
*/
static int
wlan_hdd_cfg80211_update_connect_params(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_connect_params *req,
uint32_t changed)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_update_connect_params(wiphy, dev,
req, changed);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
#if defined(WLAN_FEATURE_SAE) && \
defined(CFG80211_EXTERNAL_AUTH_SUPPORT)
#if defined(CFG80211_EXTERNAL_AUTH_AP_SUPPORT)
/**
* wlan_hdd_extauth_cache_pmkid() - Extract and cache pmkid
* @adapter: hdd vdev/net_device context
* @mac_handle: Handle to the MAC
* @params: Pointer to external auth params.
*
* Extract the PMKID and BSS from external auth params and add to the
* PMKSA Cache in CSR.
*/
static void
wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter,
mac_handle_t mac_handle,
struct cfg80211_external_auth_params *params)
{
tPmkidCacheInfo pmk_cache;
QDF_STATUS result;
if (params->pmkid) {
qdf_mem_zero(&pmk_cache, sizeof(pmk_cache));
qdf_mem_copy(pmk_cache.BSSID.bytes, params->bssid,
QDF_MAC_ADDR_SIZE);
qdf_mem_copy(pmk_cache.PMKID, params->pmkid,
PMKID_LEN);
result = wlan_hdd_set_pmksa_cache(adapter, &pmk_cache);
if (!QDF_IS_STATUS_SUCCESS(result))
hdd_debug("external_auth: Failed to cache PMKID");
}
}
#else
static void
wlan_hdd_extauth_cache_pmkid(struct hdd_adapter *adapter,
mac_handle_t mac_handle,
struct cfg80211_external_auth_params *params)
{}
#endif
/**
* __wlan_hdd_cfg80211_external_auth() - Handle external auth
*
* @wiphy: Pointer to wireless phy
* @dev: net device
* @params: Pointer to external auth params.
* Return: 0 on success, negative errno on failure
*
* Userspace sends status of the external authentication(e.g., SAE) with a peer.
* The message carries BSSID of the peer and auth status (WLAN_STATUS_SUCCESS/
* WLAN_STATUS_UNSPECIFIED_FAILURE) in params.
* Userspace may send PMKID in params, which can be used for
* further connections.
*/
static int
__wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_external_auth_params *params)
{
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
int ret;
mac_handle_t mac_handle;
struct qdf_mac_addr peer_mac_addr;
if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
hdd_err("Command not allowed in FTM mode");
return -EPERM;
}
if (wlan_hdd_validate_vdev_id(adapter->vdev_id))
return -EINVAL;
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
hdd_debug("external_auth status: %d peer mac: " QDF_MAC_ADDR_STR,
params->status, QDF_MAC_ADDR_ARRAY(params->bssid));
mac_handle = hdd_ctx->mac_handle;
qdf_mem_copy(peer_mac_addr.bytes, params->bssid, QDF_MAC_ADDR_SIZE);
wlan_hdd_extauth_cache_pmkid(adapter, mac_handle, params);
sme_handle_sae_msg(mac_handle, adapter->vdev_id, params->status,
peer_mac_addr);
return ret;
}
/**
* wlan_hdd_cfg80211_external_auth() - Handle external auth
* @wiphy: Pointer to wireless phy
* @dev: net device
* @params: Pointer to external auth params
*
* Return: 0 on success, negative errno on failure
*/
static int
wlan_hdd_cfg80211_external_auth(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_external_auth_params *params)
{
int errno;
struct osif_vdev_sync *vdev_sync;
errno = osif_vdev_sync_op_start(dev, &vdev_sync);
if (errno)
return errno;
errno = __wlan_hdd_cfg80211_external_auth(wiphy, dev, params);
osif_vdev_sync_op_stop(vdev_sync);
return errno;
}
#endif
/**
* wlan_hdd_chan_info_cb() - channel info callback
* @chan_info: struct scan_chan_info
*
* Store channel info into HDD context
*
* Return: None.
*/
static void wlan_hdd_chan_info_cb(struct scan_chan_info *info)
{
struct hdd_context *hdd_ctx;
struct scan_chan_info *chan;
uint8_t idx;
hdd_enter();
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
if (wlan_hdd_validate_context(hdd_ctx) != 0) {
hdd_err("hdd_ctx is invalid");
return;
}
if (!hdd_ctx->chan_info) {
hdd_err("chan_info is NULL");
return;
}
chan = hdd_ctx->chan_info;
for (idx = 0; idx < SIR_MAX_NUM_CHANNELS; idx++) {
if (chan[idx].freq == info->freq) {
hdd_update_chan_info(hdd_ctx, &chan[idx], info,
info->cmd_flag);
hdd_debug("cmd:%d freq:%u nf:%d cc:%u rcc:%u clk:%u cmd:%d tfc:%d index:%d",
chan[idx].cmd_flag, chan[idx].freq,
chan[idx].noise_floor,
chan[idx].cycle_count,
chan[idx].rx_clear_count,
chan[idx].clock_freq, chan[idx].cmd_flag,
chan[idx].tx_frame_count, idx);
if (chan[idx].freq == 0)
break;
}
}
hdd_exit();
}
/**
* wlan_hdd_init_chan_info() - init chan info in hdd context
* @hdd_ctx: HDD context pointer
*
* Return: none
*/
void wlan_hdd_init_chan_info(struct hdd_context *hdd_ctx)
{
uint32_t num_2g, num_5g, index = 0;
mac_handle_t mac_handle;
hdd_ctx->chan_info = NULL;
if (!hdd_ctx->config->enable_snr_monitoring) {
hdd_debug("SNR monitoring is disabled");
return;
}
hdd_ctx->chan_info =
qdf_mem_malloc(sizeof(struct scan_chan_info)
* QDF_MAX_NUM_CHAN);
if (!hdd_ctx->chan_info)
return;
mutex_init(&hdd_ctx->chan_info_lock);
num_2g = QDF_ARRAY_SIZE(hdd_channels_2_4_ghz);
for (; index < num_2g; index++) {
hdd_ctx->chan_info[index].freq =
hdd_channels_2_4_ghz[index].center_freq;
}
num_5g = QDF_ARRAY_SIZE(hdd_channels_5_ghz);
for (; (index - num_2g) < num_5g; index++) {
if (wlan_reg_is_dsrc_chan(hdd_ctx->pdev,
hdd_channels_5_ghz[index - num_2g].hw_value))
continue;
hdd_ctx->chan_info[index].freq =
hdd_channels_5_ghz[index - num_2g].center_freq;
}
index = num_2g + num_5g;
index = wlan_hdd_populate_srd_chan_info(hdd_ctx, index);
mac_handle = hdd_ctx->mac_handle;
sme_set_chan_info_callback(mac_handle,
&wlan_hdd_chan_info_cb);
}
/**
* wlan_hdd_deinit_chan_info() - deinit chan info in hdd context
* @hdd_ctx: hdd context pointer
*
* Return: none
*/
void wlan_hdd_deinit_chan_info(struct hdd_context *hdd_ctx)
{
struct scan_chan_info *chan;
chan = hdd_ctx->chan_info;
hdd_ctx->chan_info = NULL;
if (chan)
qdf_mem_free(chan);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || defined(WITH_BACKPORTS)
static enum rate_info_bw hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw)
{
switch (hdd_bw) {
case HDD_RATE_BW_5:
return RATE_INFO_BW_5;
case HDD_RATE_BW_10:
return RATE_INFO_BW_10;
case HDD_RATE_BW_20:
return RATE_INFO_BW_20;
case HDD_RATE_BW_40:
return RATE_INFO_BW_40;
case HDD_RATE_BW_80:
return RATE_INFO_BW_80;
case HDD_RATE_BW_160:
return RATE_INFO_BW_160;
}
hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw);
return RATE_INFO_BW_20;
}
void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw)
{
info->bw = hdd_map_hdd_bw_to_os(hdd_bw);
}
#else
static enum rate_info_flags hdd_map_hdd_bw_to_os(enum hdd_rate_info_bw hdd_bw)
{
switch (hdd_bw) {
case HDD_RATE_BW_5:
case HDD_RATE_BW_10:
case HDD_RATE_BW_20:
return (enum rate_info_flags)0;
case HDD_RATE_BW_40:
return RATE_INFO_FLAGS_40_MHZ_WIDTH;
case HDD_RATE_BW_80:
return RATE_INFO_FLAGS_80_MHZ_WIDTH;
case HDD_RATE_BW_160:
return RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
hdd_err("Unhandled HDD_RATE_BW: %d", hdd_bw);
return (enum rate_info_flags)0;
}
void hdd_set_rate_bw(struct rate_info *info, enum hdd_rate_info_bw hdd_bw)
{
const enum rate_info_flags all_bws =
RATE_INFO_FLAGS_40_MHZ_WIDTH |
RATE_INFO_FLAGS_80_MHZ_WIDTH |
RATE_INFO_FLAGS_80P80_MHZ_WIDTH |
RATE_INFO_FLAGS_160_MHZ_WIDTH;
info->flags &= ~all_bws;
info->flags |= hdd_map_hdd_bw_to_os(hdd_bw);
}
#endif
#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT
void hdd_send_update_owe_info_event(struct hdd_adapter *adapter,
uint8_t sta_addr[],
uint8_t *owe_ie,
uint32_t owe_ie_len)
{
struct cfg80211_update_owe_info owe_info;
struct net_device *dev = adapter->dev;
hdd_enter_dev(dev);
qdf_mem_copy(owe_info.peer, sta_addr, ETH_ALEN);
owe_info.ie = owe_ie;
owe_info.ie_len = owe_ie_len;
cfg80211_update_owe_info_event(dev, &owe_info, GFP_KERNEL);
hdd_exit();
}
#endif
/**
* struct cfg80211_ops - cfg80211_ops
*
* @add_virtual_intf: Add virtual interface
* @del_virtual_intf: Delete virtual interface
* @change_virtual_intf: Change virtual interface
* @change_station: Change station
* @add_beacon: Add beacon in sap mode
* @del_beacon: Delete beacon in sap mode
* @set_beacon: Set beacon in sap mode
* @start_ap: Start ap
* @change_beacon: Change beacon
* @stop_ap: Stop ap
* @change_bss: Change bss
* @add_key: Add key
* @get_key: Get key
* @del_key: Delete key
* @set_default_key: Set default key
* @set_channel: Set channel
* @scan: Scan
* @connect: Connect
* @disconnect: Disconnect
* @join_ibss = Join ibss
* @leave_ibss = Leave ibss
* @set_wiphy_params = Set wiphy params
* @set_tx_power = Set tx power
* @get_tx_power = get tx power
* @remain_on_channel = Remain on channel
* @cancel_remain_on_channel = Cancel remain on channel
* @mgmt_tx = Tx management frame
* @mgmt_tx_cancel_wait = Cancel management tx wait
* @set_default_mgmt_key = Set default management key
* @set_txq_params = Set tx queue parameters
* @get_station = Get station
* @set_power_mgmt = Set power management
* @del_station = Delete station
* @add_station = Add station
* @set_pmksa = Set pmksa
* @del_pmksa = Delete pmksa
* @flush_pmksa = Flush pmksa
* @update_ft_ies = Update FT IEs
* @tdls_mgmt = Tdls management
* @tdls_oper = Tdls operation
* @set_rekey_data = Set rekey data
* @sched_scan_start = Scheduled scan start
* @sched_scan_stop = Scheduled scan stop
* @resume = Resume wlan
* @suspend = Suspend wlan
* @set_mac_acl = Set mac acl
* @testmode_cmd = Test mode command
* @set_ap_chanwidth = Set AP channel bandwidth
* @dump_survey = Dump survey
* @key_mgmt_set_pmk = Set pmk key management
* @update_connect_params = Update connect params
*/
static struct cfg80211_ops wlan_hdd_cfg80211_ops = {
.add_virtual_intf = wlan_hdd_add_virtual_intf,
.del_virtual_intf = wlan_hdd_del_virtual_intf,
.change_virtual_intf = wlan_hdd_cfg80211_change_iface,
.change_station = wlan_hdd_change_station,
.start_ap = wlan_hdd_cfg80211_start_ap,
.change_beacon = wlan_hdd_cfg80211_change_beacon,
.stop_ap = wlan_hdd_cfg80211_stop_ap,
.change_bss = wlan_hdd_cfg80211_change_bss,
.add_key = wlan_hdd_cfg80211_add_key,
.get_key = wlan_hdd_cfg80211_get_key,
.del_key = wlan_hdd_cfg80211_del_key,
.set_default_key = wlan_hdd_cfg80211_set_default_key,
.scan = wlan_hdd_cfg80211_scan,
.connect = wlan_hdd_cfg80211_connect,
.disconnect = wlan_hdd_cfg80211_disconnect,
.join_ibss = wlan_hdd_cfg80211_join_ibss,
.leave_ibss = wlan_hdd_cfg80211_leave_ibss,
.set_wiphy_params = wlan_hdd_cfg80211_set_wiphy_params,
.set_tx_power = wlan_hdd_cfg80211_set_txpower,
.get_tx_power = wlan_hdd_cfg80211_get_txpower,
.remain_on_channel = wlan_hdd_cfg80211_remain_on_channel,
.cancel_remain_on_channel = wlan_hdd_cfg80211_cancel_remain_on_channel,
.mgmt_tx = wlan_hdd_mgmt_tx,
.mgmt_tx_cancel_wait = wlan_hdd_cfg80211_mgmt_tx_cancel_wait,
.set_default_mgmt_key = wlan_hdd_set_default_mgmt_key,
.set_txq_params = wlan_hdd_set_txq_params,
.dump_station = wlan_hdd_cfg80211_dump_station,
.get_station = wlan_hdd_cfg80211_get_station,
.set_power_mgmt = wlan_hdd_cfg80211_set_power_mgmt,
.del_station = wlan_hdd_cfg80211_del_station,
.add_station = wlan_hdd_cfg80211_add_station,
.set_pmksa = wlan_hdd_cfg80211_set_pmksa,
.del_pmksa = wlan_hdd_cfg80211_del_pmksa,
.flush_pmksa = wlan_hdd_cfg80211_flush_pmksa,
#if defined(KERNEL_SUPPORT_11R_CFG80211)
.update_ft_ies = wlan_hdd_cfg80211_update_ft_ies,
#endif
#ifdef CFG80211_EXTERNAL_DH_UPDATE_SUPPORT
.update_owe_info = wlan_hdd_cfg80211_update_owe_info,
#endif
#ifdef FEATURE_WLAN_TDLS
.tdls_mgmt = wlan_hdd_cfg80211_tdls_mgmt,
.tdls_oper = wlan_hdd_cfg80211_tdls_oper,
#endif
#ifdef WLAN_FEATURE_GTK_OFFLOAD
.set_rekey_data = wlan_hdd_cfg80211_set_rekey_data,
#endif /* WLAN_FEATURE_GTK_OFFLOAD */
#ifdef FEATURE_WLAN_SCAN_PNO
.sched_scan_start = wlan_hdd_cfg80211_sched_scan_start,
.sched_scan_stop = wlan_hdd_cfg80211_sched_scan_stop,
#endif /*FEATURE_WLAN_SCAN_PNO */
.resume = wlan_hdd_cfg80211_resume_wlan,
.suspend = wlan_hdd_cfg80211_suspend_wlan,
.set_mac_acl = wlan_hdd_cfg80211_set_mac_acl,
#ifdef WLAN_NL80211_TESTMODE
.testmode_cmd = wlan_hdd_cfg80211_testmode,
#endif
#ifdef QCA_HT_2040_COEX
.set_ap_chanwidth = wlan_hdd_cfg80211_set_ap_channel_width,
#endif
.dump_survey = wlan_hdd_cfg80211_dump_survey,
#ifdef CHANNEL_SWITCH_SUPPORTED
.channel_switch = wlan_hdd_cfg80211_channel_switch,
#endif
#ifdef FEATURE_MONITOR_MODE_SUPPORT
.set_monitor_channel = wlan_hdd_cfg80211_set_mon_ch,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)) || \
defined(CFG80211_ABORT_SCAN)
.abort_scan = wlan_hdd_cfg80211_abort_scan,
#endif
#if defined(CFG80211_UPDATE_CONNECT_PARAMS) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
.update_connect_params = wlan_hdd_cfg80211_update_connect_params,
#endif
#if defined(WLAN_FEATURE_SAE) && \
defined(CFG80211_EXTERNAL_AUTH_SUPPORT)
.external_auth = wlan_hdd_cfg80211_external_auth,
#endif
};