blob: 62a810fdc88056a29167af0c7ef01ab03639b0ed [file] [log] [blame]
/*
* Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*
*/
/**========================================================================
\file wlan_hdd_cfg80211.c
\brief WLAN Host Device Driver implementation
========================================================================*/
/**=========================================================================
EDIT HISTORY FOR FILE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
$Header:$ $DateTime: $ $Author: $
when who what, where, why
-------- --- --------------------------------------------------------
21/12/09 Ashwani Created module.
07/06/10 Kumar Deepak Implemented cfg80211 callbacks for ANDROID
Ganesh K
==========================================================================*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/wireless.h>
#include <linux/etherdevice.h>
#include <wlan_hdd_includes.h>
#include <net/arp.h>
#include <net/cfg80211.h>
#include <linux/wireless.h>
#include <wlan_hdd_wowl.h>
#include <aniGlobal.h>
#include "ccmApi.h"
#include "sirParams.h"
#include "dot11f.h"
#include "wlan_hdd_assoc.h"
#include "wlan_hdd_wext.h"
#include "sme_Api.h"
#include "wlan_hdd_p2p.h"
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_hostapd.h"
#include "sapInternal.h"
#include "wlan_hdd_softap_tx_rx.h"
#include "wlan_hdd_main.h"
#include "wlan_hdd_assoc.h"
#include "wlan_hdd_power.h"
#include "wlan_hdd_trace.h"
#include "vos_types.h"
#include "vos_trace.h"
#ifdef WLAN_BTAMP_FEATURE
#include "bap_hdd_misc.h"
#endif
#include <qc_sap_ioctl.h>
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_wmm.h"
#include "wlan_qct_wda.h"
#include "wlan_nv.h"
#include "wlan_hdd_dev_pwr.h"
#include "qwlan_version.h"
#include "wlan_logging_sock_svc.h"
#include "wlan_hdd_misc.h"
#define g_mode_rates_size (12)
#define a_mode_rates_size (8)
#define FREQ_BASE_80211G (2407)
#define FREQ_BAND_DIFF_80211G (5)
#define MAX_SCAN_SSID 9
#define MAX_PENDING_LOG 5
#define GET_IE_LEN_IN_BSS_DESC(lenInBss) ( lenInBss + sizeof(lenInBss) - \
((uintptr_t)OFFSET_OF( tSirBssDescription, ieFields)))
#define HDD2GHZCHAN(freq, chan, flag) { \
.band = HDD_NL80211_BAND_2GHZ, \
.center_freq = (freq), \
.hw_value = (chan),\
.flags = (flag), \
.max_antenna_gain = 0 ,\
.max_power = 30, \
}
#define HDD5GHZCHAN(freq, chan, flag) { \
.band = HDD_NL80211_BAND_5GHZ, \
.center_freq = (freq), \
.hw_value = (chan),\
.flags = (flag), \
.max_antenna_gain = 0 ,\
.max_power = 30, \
}
#define HDD_G_MODE_RATETAB(rate, rate_id, flag)\
{\
.bitrate = rate, \
.hw_value = rate_id, \
.flags = flag, \
}
#ifdef WLAN_FEATURE_VOWIFI_11R
#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
#endif
#define HDD_CHANNEL_14 14
#define WLAN_HDD_MAX_FEATURE_SET 8
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
/*
* Used to allocate the size of 4096 for the link layer stats.
* The size of 4096 is considered assuming that all data per
* respective event fit with in the limit.Please take a call
* on the limit based on the data requirements on link layer
* statistics.
*/
#define LL_STATS_EVENT_BUF_SIZE 4096
#endif
#ifdef WLAN_FEATURE_EXTSCAN
/*
* Used to allocate the size of 4096 for the EXTScan NL data.
* The size of 4096 is considered assuming that all data per
* respective event fit with in the limit.Please take a call
* on the limit based on the data requirements.
*/
#define EXTSCAN_EVENT_BUF_SIZE 4096
#define EXTSCAN_MAX_CACHED_RESULTS_PER_IND 32
#endif
/*EXT TDLS*/
/*
* Used to allocate the size of 4096 for the TDLS.
* The size of 4096 is considered assuming that all data per
* respective event fit with in the limit.Please take a call
* on the limit based on the data requirements on link layer
* statistics.
*/
#define EXTTDLS_EVENT_BUF_SIZE 4096
/*
* Values for Mac spoofing feature
*
*/
#define MAC_ADDR_SPOOFING_FW_HOST_DISABLE 0
#define MAC_ADDR_SPOOFING_FW_HOST_ENABLE 1
#define MAC_ADDR_SPOOFING_FW_ENABLE_HOST_DISABLE 2
#define MAC_ADDR_SPOOFING_DEFER_INTERVAL 10 //in ms
/*
* max_sched_scan_plans defined to 10
*/
#define MAX_SCHED_SCAN_PLANS 10
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_KRK 0x004096ff /* use for KRK */
WLAN_CIPHER_SUITE_KRK,
WLAN_CIPHER_SUITE_CCMP,
#else
WLAN_CIPHER_SUITE_CCMP,
#endif
#ifdef FEATURE_WLAN_WAPI
WLAN_CIPHER_SUITE_SMS4,
#endif
#ifdef WLAN_FEATURE_11W
WLAN_CIPHER_SUITE_AES_CMAC,
#endif
};
const static struct ieee80211_channel hdd_channels_2_4_GHZ[] =
{
HDD2GHZCHAN(2412, 1, 0) ,
HDD2GHZCHAN(2417, 2, 0) ,
HDD2GHZCHAN(2422, 3, 0) ,
HDD2GHZCHAN(2427, 4, 0) ,
HDD2GHZCHAN(2432, 5, 0) ,
HDD2GHZCHAN(2437, 6, 0) ,
HDD2GHZCHAN(2442, 7, 0) ,
HDD2GHZCHAN(2447, 8, 0) ,
HDD2GHZCHAN(2452, 9, 0) ,
HDD2GHZCHAN(2457, 10, 0) ,
HDD2GHZCHAN(2462, 11, 0) ,
HDD2GHZCHAN(2467, 12, 0) ,
HDD2GHZCHAN(2472, 13, 0) ,
HDD2GHZCHAN(2484, 14, 0) ,
};
const static struct ieee80211_channel hdd_channels_5_GHZ[] =
{
HDD5GHZCHAN(4920, 240, 0) ,
HDD5GHZCHAN(4940, 244, 0) ,
HDD5GHZCHAN(4960, 248, 0) ,
HDD5GHZCHAN(4980, 252, 0) ,
HDD5GHZCHAN(5040, 208, 0) ,
HDD5GHZCHAN(5060, 212, 0) ,
HDD5GHZCHAN(5080, 216, 0) ,
HDD5GHZCHAN(5180, 36, 0) ,
HDD5GHZCHAN(5200, 40, 0) ,
HDD5GHZCHAN(5220, 44, 0) ,
HDD5GHZCHAN(5240, 48, 0) ,
HDD5GHZCHAN(5260, 52, 0) ,
HDD5GHZCHAN(5280, 56, 0) ,
HDD5GHZCHAN(5300, 60, 0) ,
HDD5GHZCHAN(5320, 64, 0) ,
HDD5GHZCHAN(5500,100, 0) ,
HDD5GHZCHAN(5520,104, 0) ,
HDD5GHZCHAN(5540,108, 0) ,
HDD5GHZCHAN(5560,112, 0) ,
HDD5GHZCHAN(5580,116, 0) ,
HDD5GHZCHAN(5600,120, 0) ,
HDD5GHZCHAN(5620,124, 0) ,
HDD5GHZCHAN(5640,128, 0) ,
HDD5GHZCHAN(5660,132, 0) ,
HDD5GHZCHAN(5680,136, 0) ,
HDD5GHZCHAN(5700,140, 0) ,
#ifdef FEATURE_WLAN_CH144
HDD5GHZCHAN(5720,144, 0) ,
#endif /* FEATURE_WLAN_CH144 */
HDD5GHZCHAN(5745,149, 0) ,
HDD5GHZCHAN(5765,153, 0) ,
HDD5GHZCHAN(5785,157, 0) ,
HDD5GHZCHAN(5805,161, 0) ,
HDD5GHZCHAN(5825,165, 0) ,
};
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_SUP_WIDTH_20_40,
.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
.ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
.ht_cap.mcs.rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
.ht_cap.mcs.rx_highest = cpu_to_le16( 72 ),
.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
.vht_cap.vht_supported = 1,
.vht_cap.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454
| IEEE80211_VHT_CAP_SHORT_GI_80,
};
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,
.vht_cap.cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454
| IEEE80211_VHT_CAP_SHORT_GI_80,
};
/* 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),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
[NL80211_IFTYPE_ADHOC] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ACTION) |
BIT(SIR_MAC_MGMT_PROBE_REQ),
},
[NL80211_IFTYPE_P2P_GO] = {
/* This is also same as for SoftAP */
.tx = 0xffff,
.rx = BIT(SIR_MAC_MGMT_ASSOC_REQ) |
BIT(SIR_MAC_MGMT_REASSOC_REQ) |
BIT(SIR_MAC_MGMT_PROBE_REQ) |
BIT(SIR_MAC_MGMT_DISASSOC) |
BIT(SIR_MAC_MGMT_AUTH) |
BIT(SIR_MAC_MGMT_DEAUTH) |
BIT(SIR_MAC_MGMT_ACTION),
},
};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
static const struct ieee80211_iface_limit
wlan_hdd_iface_limit[] = {
{
/* max = 3 ; Our driver create two interfaces during driver init
* wlan0 and p2p0 interfaces. p2p0 is considered as station
* interface until a group is formed. In JB architecture, once the
* group is formed, interface type of p2p0 is changed to P2P GO or
* Client.
* When supplicant remove the group, it first issue a set interface
* cmd to change the mode back to Station. In JB this works fine as
* we advertize two station type interface during driver init.
* Some vendors create separate interface for P2P GO/Client,
* after group formation(Third one). But while group remove
* supplicant first tries to change the mode(3rd interface) to STATION
* But as we advertized only two sta type interfaces nl80211 was
* returning error for the third one which was leading to failure in
* delete interface. Ideally while removing the group, supplicant
* should not try to change the 3rd interface mode to Station type.
* Till we get a fix in wpa_supplicant, we advertize max STA
* interface type to 3
*/
.max = 3,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP),
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_CLIENT),
},
};
/* interface limits for sta + monitor SCC */
static const struct ieee80211_iface_limit
wlan_hdd_iface_sta_mon_limit[] = {
{
.max = 1,
.types = BIT(NL80211_IFTYPE_STATION),
},
{
.max = 1, /* Monitor interface */
.types = BIT(NL80211_IFTYPE_MONITOR),
},
};
/* By default, only single channel concurrency is allowed */
static struct ieee80211_iface_combination
wlan_hdd_iface_combination[] = {
{
.limits = wlan_hdd_iface_limit,
.num_different_channels = 1,
/*
* max = WLAN_MAX_INTERFACES ; JellyBean architecture creates wlan0
* and p2p0 interfaces during driver init
* Some vendors create separate interface for P2P operations.
* wlan0: STA interface
* p2p0: P2P Device interface, action frames goes
* through this interface.
* p2p-xx: P2P interface, After GO negotiation this interface is
* created for p2p operations(GO/CLIENT interface).
*/
.max_interfaces = WLAN_MAX_INTERFACES,
.n_limits = ARRAY_SIZE(wlan_hdd_iface_limit),
.beacon_int_infra_match = false,
},
{
.limits = wlan_hdd_iface_sta_mon_limit,
.num_different_channels = 1,
.max_interfaces = WLAN_STA_AND_MON_INTERFACES,
.n_limits = ARRAY_SIZE(wlan_hdd_iface_sta_mon_limit),
.beacon_int_infra_match = false,
}
};
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS)
static const struct wiphy_wowlan_support wowlan_support_cfg80211_init = {
.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_MAGIC_PKT,
.n_patterns = WOWL_MAX_PTRNS_ALLOWED,
.pattern_min_len = 1,
.pattern_max_len = WOWL_PTRN_MAX_SIZE,
};
#endif
static struct cfg80211_ops wlan_hdd_cfg80211_ops;
/* Data rate 100KBPS based on IE Index */
struct index_data_rate_type
{
v_U8_t beacon_rate_index;
v_U16_t supported_rate[4];
};
/* 11B, 11G Rate table include Basic rate and Extended rate
The IDX field is the rate index
The HI field is the rate when RSSI is strong or being ignored
(in this case we report actual rate)
The MID field is the rate when RSSI is moderate
(in this case we cap 11b rates at 5.5 and 11g rates at 24)
The LO field is the rate when RSSI is low
(in this case we don't report rates, actual current rate used)
*/
static const struct
{
v_U8_t beacon_rate_index;
v_U16_t supported_rate[4];
} supported_data_rate[] =
{
/* IDX HI HM LM LO (RSSI-based index */
{2, { 10, 10, 10, 0}},
{4, { 20, 20, 10, 0}},
{11, { 55, 20, 10, 0}},
{12, { 60, 55, 20, 0}},
{18, { 90, 55, 20, 0}},
{22, {110, 55, 20, 0}},
{24, {120, 90, 60, 0}},
{36, {180, 120, 60, 0}},
{44, {220, 180, 60, 0}},
{48, {240, 180, 90, 0}},
{66, {330, 180, 90, 0}},
{72, {360, 240, 90, 0}},
{96, {480, 240, 120, 0}},
{108, {540, 240, 120, 0}}
};
/* MCS Based rate table */
static struct index_data_rate_type supported_mcs_rate[] =
{
/* MCS L20 L40 S20 S40 */
{0, {65, 135, 72, 150}},
{1, {130, 270, 144, 300}},
{2, {195, 405, 217, 450}},
{3, {260, 540, 289, 600}},
{4, {390, 810, 433, 900}},
{5, {520, 1080, 578, 1200}},
{6, {585, 1215, 650, 1350}},
{7, {650, 1350, 722, 1500}}
};
#ifdef WLAN_FEATURE_11AC
#define DATA_RATE_11AC_MCS_MASK 0x03
struct index_vht_data_rate_type
{
v_U8_t beacon_rate_index;
v_U16_t supported_VHT80_rate[2];
v_U16_t supported_VHT40_rate[2];
v_U16_t supported_VHT20_rate[2];
};
typedef enum
{
DATA_RATE_11AC_MAX_MCS_7,
DATA_RATE_11AC_MAX_MCS_8,
DATA_RATE_11AC_MAX_MCS_9,
DATA_RATE_11AC_MAX_MCS_NA
} eDataRate11ACMaxMcs;
/* SSID broadcast type */
typedef enum eSSIDBcastType
{
eBCAST_UNKNOWN = 0,
eBCAST_NORMAL = 1,
eBCAST_HIDDEN = 2,
} tSSIDBcastType;
/* MCS Based VHT rate table */
static struct index_vht_data_rate_type supported_vht_mcs_rate[] =
{
/* MCS L80 S80 L40 S40 L20 S40*/
{0, {293, 325}, {135, 150}, {65, 72}},
{1, {585, 650}, {270, 300}, {130, 144}},
{2, {878, 975}, {405, 450}, {195, 217}},
{3, {1170, 1300}, {540, 600}, {260, 289}},
{4, {1755, 1950}, {810, 900}, {390, 433}},
{5, {2340, 2600}, {1080, 1200}, {520, 578}},
{6, {2633, 2925}, {1215, 1350}, {585, 650}},
{7, {2925, 3250}, {1350, 1500}, {650, 722}},
{8, {3510, 3900}, {1620, 1800}, {780, 867}},
{9, {3900, 4333}, {1800, 2000}, {780, 867}}
};
#endif /* WLAN_FEATURE_11AC */
/*array index points to MCS and array value points respective rssi*/
static int rssiMcsTbl[][10] =
{
/*MCS 0 1 2 3 4 5 6 7 8 9*/
{-82, -79, -77, -74, -70, -66, -65, -64, -59, -57}, //20
{-79, -76, -74, -71, -67, -63, -62, -61, -56, -54}, //40
{-76, -73, -71, -68, -64, -60, -59, -58, -53, -51} //80
};
extern struct net_device_ops net_ops_struct;
#ifdef FEATURE_WLAN_SCAN_PNO
static eHalStatus wlan_hdd_is_pno_allowed(hdd_adapter_t *pAdapter);
#endif
#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_TYPE = 3,
/* 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_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 */
#ifdef FEATURE_WLAN_CH_AVOID
/*
* 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(hdd_context_t *pHddCtx,
tHddAvoidFreqList *pAvoidFreqList)
{
struct sk_buff *vendor_event;
ENTER();
if (!pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is null", __func__);
return -1;
}
if (!pAvoidFreqList)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pAvoidFreqList is null", __func__);
return -1;
}
vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
sizeof(tHddAvoidFreqList),
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_INDEX,
GFP_KERNEL);
if (!vendor_event)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_event_alloc failed", __func__);
return -1;
}
memcpy(skb_put(vendor_event, sizeof(tHddAvoidFreqList)),
(void *)pAvoidFreqList, sizeof(tHddAvoidFreqList));
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
return 0;
}
#endif /* FEATURE_WLAN_CH_AVOID */
/*
* 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 vos_hang_reason reason)
{
unsigned int ret_val;
switch (reason) {
case VOS_GET_MSG_BUFF_FAILURE:
ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE;
break;
case VOS_ACTIVE_LIST_TIMEOUT:
ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT;
break;
case VOS_SCAN_REQ_EXPIRED:
ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED;
break;
case VOS_TRANSMISSIONS_TIMEOUT:
ret_val = QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT;
break;
case VOS_DXE_FAILURE:
ret_val = QCA_WLAN_HANG_DXE_FAILURE;
break;
case VOS_WDI_FAILURE:
ret_val = QCA_WLAN_HANG_WDI_FAILURE;
break;
case VOS_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(hdd_context_t *hdd_ctx,
enum vos_hang_reason reason)
{
struct sk_buff *vendor_event;
enum qca_wlan_vendor_hang_reason hang_reason;
ENTER();
if (!hdd_ctx) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"HDD context is null");
return -EINVAL;
}
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
sizeof(unsigned int),
HANG_REASON_INDEX,
GFP_KERNEL);
if (!vendor_event) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"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,
(unsigned int) hang_reason)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail");
kfree_skb(vendor_event);
return -EINVAL;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
return 0;
}
#undef HANG_REASON_INDEX
/*
* FUNCTION: __wlan_hdd_cfg80211_nan_request
* This is called when wlan driver needs to send vendor specific
* nan request event.
*/
static int __wlan_hdd_cfg80211_nan_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
tNanRequestReq nan_req;
VOS_STATUS status;
int ret_val = -1;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
if (0 == data_len)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("NAN - Invalid Request, length = 0"));
return ret_val;
}
if (NULL == data)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("NAN - Invalid Request, data is NULL"));
return ret_val;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("HDD context is not valid"));
return -EINVAL;
}
hddLog(LOG1, FL("Received NAN command"));
vos_trace_hex_dump( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
(tANI_U8 *)data, data_len);
/* check the NAN Capability */
if (TRUE != sme_IsFeatureSupportedByFW(NAN))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("NAN is not supported by Firmware"));
return -EINVAL;
}
nan_req.request_data_len = data_len;
nan_req.request_data = data;
status = sme_NanRequest(hHal, &nan_req, pAdapter->sessionId);
if (VOS_STATUS_SUCCESS == status)
{
ret_val = 0;
}
return ret_val;
}
/*
* FUNCTION: wlan_hdd_cfg80211_nan_request
* Wrapper to protect the nan vendor command from ssr
*/
static int wlan_hdd_cfg80211_nan_request(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_nan_request(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: wlan_hdd_cfg80211_nan_callback
* This is a callback function and it gets called
* when we need to report nan response event to
* upper layers.
*/
static void wlan_hdd_cfg80211_nan_callback(void* ctx, tSirNanEvent* msg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *vendor_event;
int status;
tSirNanEvent *data;
ENTER();
if (NULL == msg)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL(" msg received here is null"));
return;
}
data = msg;
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("HDD context is not valid"));
return;
}
vendor_event = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
data->event_data_len +
NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX,
GFP_KERNEL);
if (!vendor_event)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NAN,
data->event_data_len, data->event_data))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR_NAN put fail"));
kfree_skb(vendor_event);
return;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
}
/*
* FUNCTION: wlan_hdd_cfg80211_nan_init
* This function is called to register the callback to sme layer
*/
inline void wlan_hdd_cfg80211_nan_init(hdd_context_t *pHddCtx)
{
sme_NanRegisterCallback(pHddCtx->hHal, wlan_hdd_cfg80211_nan_callback);
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_get_station_cmd()
*/
#define STATION_INVALID \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INVALID
#define STATION_INFO \
QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO
#define STATION_ASSOC_FAIL_REASON \
QCA_WLAN_VENDOR_ATTR_GET_STATION_ASSOC_FAIL_REASON
#define STATION_REMOTE \
QCA_WLAN_VENDOR_ATTR_GET_STATION_REMOTE
#define STATION_MAX \
QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX
static const struct nla_policy
hdd_get_station_policy[STATION_MAX + 1] = {
[STATION_INFO] = {.type = NLA_FLAG},
[STATION_ASSOC_FAIL_REASON] = {.type = NLA_FLAG},
};
/**
* hdd_get_station_assoc_fail() - Handle get station assoc fail
* @hdd_ctx: HDD context within host driver
* @wdev: wireless device
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION_ASSOC_FAIL.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int hdd_get_station_assoc_fail(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
struct sk_buff *skb = NULL;
uint32_t nl_buf_len;
hdd_station_ctx_t *hdd_sta_ctx;
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += sizeof(uint32_t);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (nla_put_u32(skb, INFO_ASSOC_FAIL_REASON,
hdd_sta_ctx->conn_info.assoc_status_code)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* hdd_map_auth_type() - transform auth type specific to
* vendor command
* @auth_type: csr auth type
*
* Return: Success(0) or reason code for failure
*/
static int hdd_convert_auth_type(uint32_t auth_type)
{
uint32_t ret_val;
switch (auth_type) {
case eCSR_AUTH_TYPE_OPEN_SYSTEM:
ret_val = QCA_WLAN_AUTH_TYPE_OPEN;
break;
case eCSR_AUTH_TYPE_SHARED_KEY:
ret_val = QCA_WLAN_AUTH_TYPE_SHARED;
break;
case eCSR_AUTH_TYPE_WPA:
ret_val = QCA_WLAN_AUTH_TYPE_WPA;
break;
case eCSR_AUTH_TYPE_WPA_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_WPA_PSK;
break;
case eCSR_AUTH_TYPE_AUTOSWITCH:
ret_val = QCA_WLAN_AUTH_TYPE_AUTOSWITCH;
break;
case eCSR_AUTH_TYPE_WPA_NONE:
ret_val = QCA_WLAN_AUTH_TYPE_WPA_NONE;
break;
case eCSR_AUTH_TYPE_RSN:
ret_val = QCA_WLAN_AUTH_TYPE_RSN;
break;
case eCSR_AUTH_TYPE_RSN_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_RSN_PSK;
break;
case eCSR_AUTH_TYPE_FT_RSN:
ret_val = QCA_WLAN_AUTH_TYPE_FT;
break;
case eCSR_AUTH_TYPE_FT_RSN_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_FT_PSK;
break;
case eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE:
ret_val = QCA_WLAN_AUTH_TYPE_WAI;
break;
case eCSR_AUTH_TYPE_WAPI_WAI_PSK:
ret_val = QCA_WLAN_AUTH_TYPE_WAI_PSK;
break;
#ifdef FEATURE_WLAN_ESE
case eCSR_AUTH_TYPE_CCKM_WPA:
ret_val = QCA_WLAN_AUTH_TYPE_CCKM_WPA;
break;
case eCSR_AUTH_TYPE_CCKM_RSN:
ret_val = QCA_WLAN_AUTH_TYPE_CCKM_RSN;
break;
#endif
case eCSR_AUTH_TYPE_RSN_PSK_SHA256:
ret_val = QCA_WLAN_AUTH_TYPE_SHA256_PSK;
break;
case eCSR_AUTH_TYPE_RSN_8021X_SHA256:
ret_val = QCA_WLAN_AUTH_TYPE_SHA256;
break;
case eCSR_NUM_OF_SUPPORT_AUTH_TYPE:
case eCSR_AUTH_TYPE_FAILED:
case eCSR_AUTH_TYPE_NONE:
default:
ret_val = QCA_WLAN_AUTH_TYPE_INVALID;
break;
}
return ret_val;
}
/**
* hdd_map_dot_11_mode() - transform dot11mode type specific to
* vendor command
* @dot11mode: dot11mode
*
* Return: Success(0) or reason code for failure
*/
static int hdd_convert_dot11mode(uint32_t dot11mode)
{
uint32_t ret_val;
switch (dot11mode) {
case eCSR_CFG_DOT11_MODE_11A:
ret_val = QCA_WLAN_802_11_MODE_11A;
break;
case eCSR_CFG_DOT11_MODE_11B:
ret_val = QCA_WLAN_802_11_MODE_11B;
break;
case eCSR_CFG_DOT11_MODE_11G:
ret_val = QCA_WLAN_802_11_MODE_11G;
break;
case eCSR_CFG_DOT11_MODE_11N:
ret_val = QCA_WLAN_802_11_MODE_11N;
break;
case eCSR_CFG_DOT11_MODE_11AC:
ret_val = QCA_WLAN_802_11_MODE_11AC;
break;
case eCSR_CFG_DOT11_MODE_AUTO:
case eCSR_CFG_DOT11_MODE_ABG:
default:
ret_val = QCA_WLAN_802_11_MODE_INVALID;
}
return ret_val;
}
/**
* hdd_add_tx_bitrate() - add tx bitrate attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_tx_bitrate(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx,
int idx)
{
struct nlattr *nla_attr;
uint32_t bitrate, bitrate_compat;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
bitrate = cfg80211_calculate_bitrate(
&hdd_sta_ctx->cache_conn_info.txrate);
/* report 16-bit bitrate only if we can */
bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
if (bitrate > 0 &&
nla_put_u32(skb, NL80211_RATE_INFO_BITRATE32, bitrate)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (bitrate_compat > 0 &&
nla_put_u16(skb, NL80211_RATE_INFO_BITRATE, bitrate_compat)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS,
hdd_sta_ctx->cache_conn_info.txrate.nss)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_sta_info() - add station info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_sta_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL,
(hdd_sta_ctx->cache_conn_info.signal + 100))) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_add_tx_bitrate(skb, hdd_sta_ctx, NL80211_STA_INFO_TX_BITRATE))
goto fail;
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_survey_info() - add survey info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t hdd_add_survey_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY,
hdd_sta_ctx->cache_conn_info.freq) ||
nla_put_u8(skb, NL80211_SURVEY_INFO_NOISE,
(hdd_sta_ctx->cache_conn_info.noise + 100))) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_link_standard_info() - add link info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_add_link_standard_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put(skb,
NL80211_ATTR_SSID,
hdd_sta_ctx->cache_conn_info.SSID.SSID.length,
hdd_sta_ctx->cache_conn_info.SSID.SSID.ssId)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (nla_put(skb, NL80211_ATTR_MAC, VOS_MAC_ADDR_SIZE,
hdd_sta_ctx->cache_conn_info.bssId))
goto fail;
if (hdd_add_survey_info(skb, hdd_sta_ctx, NL80211_ATTR_SURVEY_INFO))
goto fail;
if (hdd_add_sta_info(skb, hdd_sta_ctx, NL80211_ATTR_STA_INFO))
goto fail;
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_ap_standard_info() - add ap info attribute
* @skb: pointer to sk buff
* @hdd_sta_ctx: pointer to hdd station context
* @idx: attribute index
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_add_ap_standard_info(struct sk_buff *skb,
hdd_station_ctx_t *hdd_sta_ctx, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present)
if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY,
sizeof(hdd_sta_ctx->cache_conn_info.vht_caps),
&hdd_sta_ctx->cache_conn_info.vht_caps)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present)
if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY,
sizeof(hdd_sta_ctx->cache_conn_info.ht_caps),
&hdd_sta_ctx->cache_conn_info.ht_caps)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_get_station_info() - send BSS information to supplicant
* @hdd_ctx: pointer to hdd context
* @adapter: pointer to adapter
*
* Return: 0 if success else error status
*/
static int hdd_get_station_info(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
struct sk_buff *skb = NULL;
uint8_t *tmp_hs20 = NULL;
uint32_t nl_buf_len;
hdd_station_ctx_t *hdd_sta_ctx;
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.SSID.SSID.length) +
VOS_MAC_ADDR_SIZE +
sizeof(hdd_sta_ctx->cache_conn_info.freq) +
sizeof(hdd_sta_ctx->cache_conn_info.noise) +
sizeof(hdd_sta_ctx->cache_conn_info.signal) +
(sizeof(uint32_t) * 2) +
sizeof(hdd_sta_ctx->cache_conn_info.txrate.nss) +
sizeof(hdd_sta_ctx->cache_conn_info.roam_count) +
sizeof(hdd_sta_ctx->cache_conn_info.authType) +
sizeof(hdd_sta_ctx->cache_conn_info.dot11Mode);
if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_present)
nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.vht_caps);
if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_present)
nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.ht_caps);
if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present) {
tmp_hs20 = (uint8_t *)&(hdd_sta_ctx->
cache_conn_info.hs20vendor_ie);
nl_buf_len +=
(sizeof(hdd_sta_ctx->cache_conn_info.hs20vendor_ie) -
1);
}
if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present)
nl_buf_len += sizeof(hdd_sta_ctx->cache_conn_info.ht_operation);
if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present)
nl_buf_len +=
sizeof(hdd_sta_ctx->cache_conn_info.vht_operation);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"%s: %d cfg80211_vendor_cmd_alloc_reply_skb failed",
__func__, __LINE__);
return -ENOMEM;
}
if (hdd_add_link_standard_info(skb, hdd_sta_ctx,
LINK_INFO_STANDARD_NL80211_ATTR)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_add_ap_standard_info(skb, hdd_sta_ctx,
AP_INFO_STANDARD_NL80211_ATTR)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (nla_put_u32(skb, INFO_ROAM_COUNT,
hdd_sta_ctx->cache_conn_info.roam_count) ||
nla_put_u32(skb, INFO_AKM,
hdd_convert_auth_type(
hdd_sta_ctx->cache_conn_info.authType)) ||
nla_put_u32(skb, WLAN802_11_MODE,
hdd_convert_dot11mode(
hdd_sta_ctx->cache_conn_info.dot11Mode))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_sta_ctx->cache_conn_info.conn_flag.ht_op_present)
if (nla_put(skb, HT_OPERATION,
(sizeof(hdd_sta_ctx->cache_conn_info.ht_operation)),
&hdd_sta_ctx->cache_conn_info.ht_operation)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_sta_ctx->cache_conn_info.conn_flag.vht_op_present)
if (nla_put(skb, VHT_OPERATION,
(sizeof(hdd_sta_ctx->
cache_conn_info.vht_operation)),
&hdd_sta_ctx->cache_conn_info.vht_operation)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
if (hdd_sta_ctx->cache_conn_info.conn_flag.hs20_present)
if (nla_put(skb, AP_INFO_HS20_INDICATION,
(sizeof(hdd_sta_ctx->cache_conn_info.hs20vendor_ie)
- 1), tmp_hs20 + 1)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"put fail");
goto fail;
}
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* hdd_add_survey_info_sap_get_len - get data length used in
* hdd_add_survey_info_sap()
*
* This function calculates the data length used in hdd_add_survey_info_sap()
*
* Return: total data length used in hdd_add_survey_info_sap()
*/
static uint32_t hdd_add_survey_info_sap_get_len(void)
{
return ((NLA_HDRLEN) + (sizeof(uint32_t) + NLA_HDRLEN));
}
/**
* hdd_add_survey_info - add survey info attribute
* @skb: pointer to response skb buffer
* @stainfo: station information
* @idx: attribute type index for nla_next_start()
*
* This function adds survey info attribute to response skb buffer
*
* Return : 0 on success and errno on failure
*/
static int32_t hdd_add_survey_info_sap(struct sk_buff *skb,
struct hdd_cache_sta_info *stainfo,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u32(skb, NL80211_SURVEY_INFO_FREQUENCY,
stainfo->freq)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_tx_bitrate_sap_get_len - get data length used in
* hdd_add_tx_bitrate_sap()
*
* This function calculates the data length used in hdd_add_tx_bitrate_sap()
*
* Return: total data length used in hdd_add_tx_bitrate_sap()
*/
static uint32_t hdd_add_tx_bitrate_sap_get_len(void)
{
return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN));
}
/**
* hdd_add_tx_bitrate_sap - add vht nss info attribute
* @skb: pointer to response skb buffer
* @stainfo: station information
* @idx: attribute type index for nla_next_start()
*
* This function adds vht nss attribute to response skb buffer
*
* Return : 0 on success and errno on failure
*/
static int hdd_add_tx_bitrate_sap(struct sk_buff *skb,
struct hdd_cache_sta_info *stainfo,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (nla_put_u8(skb, NL80211_RATE_INFO_VHT_NSS,
stainfo->nss)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_sta_info_sap_get_len - get data length used in
* hdd_add_sta_info_sap()
*
* This function calculates the data length used in hdd_add_sta_info_sap()
*
* Return: total data length used in hdd_add_sta_info_sap()
*/
static uint32_t hdd_add_sta_info_sap_get_len(void)
{
return ((NLA_HDRLEN) + (sizeof(uint8_t) + NLA_HDRLEN) +
hdd_add_tx_bitrate_sap_get_len());
}
/**
* hdd_add_sta_info_sap - add sta signal info attribute
* @skb: pointer to response skb buffer
* @rssi: peer rssi value
* @stainfo: station information
* @idx: attribute type index for nla_next_start()
*
* This function adds sta signal attribute to response skb buffer
*
* Return : 0 on success and errno on failure
*/
static int32_t hdd_add_sta_info_sap(struct sk_buff *skb, int8_t rssi,
struct hdd_cache_sta_info *stainfo, int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
/* upperlayer expects positive rssi value */
if (nla_put_u8(skb, NL80211_STA_INFO_SIGNAL, (rssi + 96))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
if (hdd_add_tx_bitrate_sap(skb, stainfo, NL80211_STA_INFO_TX_BITRATE)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_link_standard_info_sap_get_len - get data length used in
* hdd_add_link_standard_info_sap()
*
* This function calculates the data length used in
* hdd_add_link_standard_info_sap()
*
* Return: total data length used in hdd_add_link_standard_info_sap()
*/
static uint32_t hdd_add_link_standard_info_sap_get_len(void)
{
return ((NLA_HDRLEN) +
hdd_add_survey_info_sap_get_len() +
hdd_add_sta_info_sap_get_len() +
(sizeof(uint32_t) + NLA_HDRLEN));
}
/**
* hdd_add_link_standard_info_sap - add add link info attribut
* @skb: pointer to response skb buffer
* @stainfo: station information
* @idx: attribute type index for nla_next_start()
*
* This function adds link info attribut to response skb buffer
*
* Return : 0 on success and errno on failure
*/
static int hdd_add_link_standard_info_sap(struct sk_buff *skb, int8_t rssi,
struct hdd_cache_sta_info *stainfo,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (hdd_add_survey_info_sap(skb, stainfo, NL80211_ATTR_SURVEY_INFO))
goto fail;
if (hdd_add_sta_info_sap(skb, rssi, stainfo, NL80211_ATTR_STA_INFO))
goto fail;
if (nla_put_u32(skb, NL80211_ATTR_REASON_CODE, stainfo->reason_code)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_add_ap_standard_info_sap_get_len - get data length used in
* hdd_add_ap_standard_info_sap()
* @stainfo: station information
*
* This function calculates the data length used in
* hdd_add_ap_standard_info_sap()
*
* Return: total data length used in hdd_add_ap_standard_info_sap()
*/
static uint32_t hdd_add_ap_standard_info_sap_get_len(
struct hdd_cache_sta_info *stainfo)
{
uint32_t len;
len = NLA_HDRLEN;
if (stainfo->vht_present)
len += (sizeof(stainfo->vht_caps) + NLA_HDRLEN);
if (stainfo->ht_present)
len += (sizeof(stainfo->ht_caps) + NLA_HDRLEN);
return len;
}
/**
* hdd_add_ap_standard_info_sap - add HT and VHT info attributes
* @skb: pointer to response skb buffer
* @stainfo: station information
* @idx: attribute type index for nla_next_start()
*
* This function adds HT and VHT info attributes to response skb buffer
*
* Return : 0 on success and errno on failure
*/
static int hdd_add_ap_standard_info_sap(struct sk_buff *skb,
struct hdd_cache_sta_info *stainfo,
int idx)
{
struct nlattr *nla_attr;
nla_attr = nla_nest_start(skb, idx);
if (!nla_attr)
goto fail;
if (stainfo->vht_present) {
if (nla_put(skb, NL80211_ATTR_VHT_CAPABILITY,
sizeof(stainfo->vht_caps),
&stainfo->vht_caps)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
}
if (stainfo->ht_present) {
if (nla_put(skb, NL80211_ATTR_HT_CAPABILITY,
sizeof(stainfo->ht_caps),
&stainfo->ht_caps)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("put fail"));
goto fail;
}
}
nla_nest_end(skb, nla_attr);
return 0;
fail:
return -EINVAL;
}
/**
* hdd_decode_ch_width - decode channel band width based
* @ch_width: encoded enum value holding channel band width
*
* This function decodes channel band width from the given encoded enum value.
*
* Returns: decoded channel band width.
*/
static uint8_t hdd_decode_ch_width(tSirMacHTChannelWidth ch_width)
{
switch (ch_width) {
case 0:
return 20;
case 1:
return 40;
case 2:
return 80;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"invalid enum: %d", ch_width);
return 20;
}
}
/**
* hdd_get_cached_station_remote() - get cached(deleted) peer's info
* @hdd_ctx: hdd context
* @adapter: hostapd interface
* @mac_addr: mac address of requested peer
*
* This function collect and indicate the cached(deleted) peer's info
*
* Return: 0 on success, otherwise error value
*/
static int hdd_get_cached_station_remote(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
v_MACADDR_t mac_addr)
{
struct hdd_cache_sta_info *stainfo;
struct sk_buff *skb = NULL;
uint32_t nl_buf_len;
uint8_t cw;
ptSapContext sap_ctx;
v_CONTEXT_t vos_ctx = (WLAN_HDD_GET_CTX(adapter))->pvosContext;
sap_ctx = VOS_GET_SAP_CB(vos_ctx);
if(sap_ctx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("psapCtx is NULL"));
return -ENOENT;
}
stainfo = hdd_get_cache_stainfo(sap_ctx->cache_sta_info,
mac_addr.bytes);
if (!stainfo) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"peer " MAC_ADDRESS_STR " not found",
MAC_ADDR_ARRAY(mac_addr.bytes));
return -EINVAL;
}
if (sap_ctx->aStaInfo[stainfo->ucSTAId].isUsed == TRUE &&
!sap_ctx->aStaInfo[stainfo->ucSTAId].isDeauthInProgress) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"peer " MAC_ADDRESS_STR " is in connected state",
MAC_ADDR_ARRAY(mac_addr.bytes));
return -EINVAL;
}
nl_buf_len = NLMSG_HDRLEN + hdd_add_link_standard_info_sap_get_len() +
hdd_add_ap_standard_info_sap_get_len(stainfo) +
(sizeof(stainfo->dot11_mode) + NLA_HDRLEN) +
(sizeof(cw) + NLA_HDRLEN) +
(sizeof(stainfo->rx_rate) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
if (!skb) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "cfg80211_vendor_cmd_alloc_reply_skb failed");
return -ENOMEM;
}
if (hdd_add_link_standard_info_sap(skb, stainfo->rssi, stainfo,
LINK_INFO_STANDARD_NL80211_ATTR)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "link standard put fail");
goto fail;
}
if (hdd_add_ap_standard_info_sap(skb, stainfo,
AP_INFO_STANDARD_NL80211_ATTR)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "ap standard put fail");
goto fail;
}
/* upper layer expects decoded channel BW */
cw = hdd_decode_ch_width(stainfo->ch_width);
if (nla_put_u32(skb, REMOTE_SUPPORTED_MODE, stainfo->dot11_mode) ||
nla_put_u8(skb, REMOTE_CH_WIDTH, cw)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "remote ch put fail");
goto fail;
}
if (nla_put_u32(skb, REMOTE_LAST_RX_RATE, (stainfo->rx_rate * 100))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "rx rate put fail");
goto fail;
}
vos_mem_zero(stainfo, sizeof(*stainfo));
return cfg80211_vendor_cmd_reply(skb);
fail:
if (skb)
kfree_skb(skb);
return -EINVAL;
}
/**
* __hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int32_t
__hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX + 1];
int32_t status;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"Enter");
if (VOS_FTM_MODE == hdd_get_conparam()) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"Command not allowed in FTM mode");
status = -EPERM;
goto out;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
goto out;
status = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_GET_STATION_MAX,
data, data_len, NULL);
if (status) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"Invalid ATTR");
goto out;
}
/* Parse and fetch Command Type*/
if (tb[STATION_INFO]) {
status = hdd_get_station_info(hdd_ctx, adapter);
} else if (tb[STATION_ASSOC_FAIL_REASON]) {
status = hdd_get_station_assoc_fail(hdd_ctx, adapter);
} else if (tb[STATION_REMOTE]) {
v_MACADDR_t mac_addr;
if (adapter->device_mode != WLAN_HDD_SOFTAP &&
adapter->device_mode != WLAN_HDD_P2P_GO) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"invalid device_mode:%d",
adapter->device_mode);
status = -EINVAL;
goto out;
}
nla_memcpy(mac_addr.bytes, tb[STATION_REMOTE],
VOS_MAC_ADDRESS_LEN);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "STATION_REMOTE "MAC_ADDRESS_STR"",
MAC_ADDR_ARRAY(mac_addr.bytes));
status = hdd_get_cached_station_remote(hdd_ctx, adapter,
mac_addr);
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,"get station info cmd type failed");
status = -EINVAL;
goto out;
}
EXIT();
out:
return status;
}
/**
* wlan_hdd_cfg80211_get_station_cmd() - Handle get station vendor cmd
* @wiphy: corestack handler
* @wdev: wireless device
* @data: data
* @data_len: data length
*
* Handles QCA_NL80211_VENDOR_SUBCMD_GET_STATION.
* Validate cmd attributes and send the station info to upper layers.
*
* Return: Success(0) or reason code for failure
*/
static int32_t
hdd_cfg80211_get_station_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_cfg80211_get_station_cmd(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* undef short names defined for get station command
* used by __wlan_hdd_cfg80211_get_station_cmd()
*/
#undef STATION_INVALID
#undef STATION_INFO
#undef STATION_ASSOC_FAIL_REASON
#undef STATION_MAX
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
static v_BOOL_t put_wifi_rate_stat( tpSirWifiRateStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE,
stats->rate.preamble) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS,
stats->rate.nss) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW,
stats->rate.bw) ||
nla_put_u8(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX,
stats->rate.rateMcsIdx) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE,
stats->rate.bitrate ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU,
stats->txMpdu ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU,
stats->rxMpdu ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST,
stats->mpduLost ) ||
nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES,
stats->retries) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT,
stats->retriesShort ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG,
stats->retriesLong))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return FALSE;
}
return TRUE;
}
static v_BOOL_t put_wifi_peer_info( tpSirWifiPeerInfo stats,
struct sk_buff *vendor_event)
{
u32 i = 0;
struct nlattr *rateInfo;
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE,
stats->type) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS,
VOS_MAC_ADDR_SIZE, &stats->peerMacAddress[0]) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES,
stats->capabilities) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES,
stats->numRate))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
goto error;
}
rateInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO);
if(!rateInfo)
return FALSE;
for (i = 0; i < stats->numRate; i++)
{
struct nlattr *rates;
tpSirWifiRateStat pRateStats = (tpSirWifiRateStat )((uint8 *)
stats->rateStats +
(i * sizeof(tSirWifiRateStat)));
rates = nla_nest_start(vendor_event, i);
if(!rates)
return FALSE;
if (FALSE == put_wifi_rate_stat(pRateStats, vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
return FALSE;
}
nla_nest_end(vendor_event, rates);
}
nla_nest_end(vendor_event, rateInfo);
return TRUE;
error:
return FALSE;
}
static v_BOOL_t put_wifi_wmm_ac_stat( tpSirWifiWmmAcStat stats,
struct sk_buff *vendor_event)
{
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC,
stats->ac ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU,
stats->txMpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU,
stats->rxMpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST,
stats->txMcast ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST,
stats->rxMcast ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU,
stats->rxAmpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU,
stats->txAmpdu ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST,
stats->mpduLost )||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES,
stats->retries ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT,
stats->retriesShort ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG,
stats->retriesLong ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN,
stats->contentionTimeMin ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX,
stats->contentionTimeMax ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG,
stats->contentionTimeAvg ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES,
stats->contentionNumSamples ))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
return TRUE;
}
static v_BOOL_t put_wifi_interface_info(tpSirWifiInterfaceInfo stats,
struct sk_buff *vendor_event)
{
if (nla_put_s32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE, stats->mode ) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR,
VOS_MAC_ADDR_SIZE, stats->macAddr) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE,
stats->state ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING,
stats->roaming ) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES,
stats->capabilities ) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID,
strlen(stats->ssid), stats->ssid) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID,
WNI_CFG_BSSID_LEN, stats->bssid) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->apCountryStr) ||
nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR,
WNI_CFG_COUNTRY_CODE_LEN, stats->countryStr)
)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
return TRUE;
}
static v_BOOL_t put_wifi_iface_stats(hdd_adapter_t *pAdapter,
tpSirWifiIfaceStat pWifiIfaceStat,
struct sk_buff *vendor_event)
{
int i = 0;
struct nlattr *wmmInfo;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
WLANTL_InterfaceStatsType *pWifiIfaceStatTL = NULL;
tSirWifiWmmAcStat accessclassStats;
if (FALSE == put_wifi_interface_info(
&pWifiIfaceStat->info,
vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail") );
return FALSE;
}
pWifiIfaceStatTL = (WLANTL_InterfaceStatsType *)
vos_mem_malloc(sizeof(WLANTL_InterfaceStatsType));
if (NULL == pWifiIfaceStatTL)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos_mem_malloc failed"));
return FALSE;
}
accessclassStats = pWifiIfaceStat->AccessclassStats[WIFI_AC_BK];
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK] =
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE];
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE] = accessclassStats;
accessclassStats.ac = pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].ac;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].ac =
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].ac;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].ac = accessclassStats.ac;
if ( pWifiIfaceStat->info.state == WIFI_ASSOCIATED)
{
if (VOS_STATUS_SUCCESS ==
WLANTL_CollectInterfaceStats((WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
pHddStaCtx->conn_info.staId[0], pWifiIfaceStatTL))
{
/* mgmtRx, MgmtActionRx, rxMcast, rxMpdu, rxAmpdu, rssiData are
* obtained from TL structure
*/
pWifiIfaceStat->mgmtRx = pWifiIfaceStat->beaconRx +
pWifiIfaceStatTL->mgmtRx;
pWifiIfaceStat->rssiData = pWifiIfaceStatTL->rssiData;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VO].rxMcast
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VO].rxMcast;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VI].rxMcast
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VI].rxMcast;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].rxMcast
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BE].rxMcast;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].rxMcast
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BK].rxMcast;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VO].rxMpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VO].rxMpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VI].rxMpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VI].rxMpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].rxMpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BE].rxMpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].rxMpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BK].rxMpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VO].rxAmpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VO].rxAmpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_VI].rxAmpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_VI].rxAmpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].rxAmpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BE].rxAmpdu;
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].rxAmpdu
= pWifiIfaceStatTL->accessCategoryStats[WLANTL_AC_BK].rxAmpdu;
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Error in getting stats from TL"));
}
pWifiIfaceStat->AccessclassStats[WIFI_AC_VO].txMcast =
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_VO];
pWifiIfaceStat->AccessclassStats[WIFI_AC_VI].txMcast =
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_VI];
pWifiIfaceStat->AccessclassStats[WIFI_AC_BE].txMcast =
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_BE];
pWifiIfaceStat->AccessclassStats[WIFI_AC_BK].txMcast =
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_BK];
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Interface not Associated"));
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_IFACE) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX,
pWifiIfaceStat->beaconRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX,
pWifiIfaceStat->mgmtRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX,
pWifiIfaceStat->mgmtActionRx) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX,
pWifiIfaceStat->mgmtActionTx) ||
nla_put_s32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT,
pWifiIfaceStat->rssiMgmt) ||
nla_put_s32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA,
pWifiIfaceStat->rssiData) ||
nla_put_s32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK,
pWifiIfaceStat->rssiAck))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
vos_mem_free(pWifiIfaceStatTL);
return FALSE;
}
#ifdef FEATURE_EXT_LL_STAT
/*
* Ensure when EXT_LL_STAT is supported by both host and fwr,
* then host should send Leaky AP stats to upper layer,
* otherwise no need to send these stats.
*/
if(sme_IsFeatureSupportedByFW(EXT_LL_STAT) &&
sme_IsFeatureSupportedByDriver(EXT_LL_STAT)
)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("EXT_LL_STAT is supported by fwr and host %u %u %u %llu"),
pWifiIfaceStat->leakyApStat.is_leaky_ap,
pWifiIfaceStat->leakyApStat.avg_rx_frms_leaked,
pWifiIfaceStat->leakyApStat.rx_leak_window,
pWifiIfaceStat->leakyApStat.avg_bcn_spread);
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED,
pWifiIfaceStat->leakyApStat.is_leaky_ap) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED,
pWifiIfaceStat->leakyApStat.avg_rx_frms_leaked) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME,
pWifiIfaceStat->leakyApStat.rx_leak_window) ||
hdd_wlan_nla_put_u64(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET,
pWifiIfaceStat->leakyApStat.avg_bcn_spread))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("EXT_LL_STAT put fail"));
vos_mem_free(pWifiIfaceStatTL);
return FALSE;
}
}
#endif
wmmInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO);
if(!wmmInfo)
{
vos_mem_free(pWifiIfaceStatTL);
return FALSE;
}
for (i = 0; i < WIFI_AC_MAX; i++)
{
struct nlattr *wmmStats;
wmmStats = nla_nest_start(vendor_event, i);
if(!wmmStats)
{
vos_mem_free(pWifiIfaceStatTL);
return FALSE;
}
if (FALSE == put_wifi_wmm_ac_stat(
&pWifiIfaceStat->AccessclassStats[i],
vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put Fail"));
vos_mem_free(pWifiIfaceStatTL);
return FALSE;
}
nla_nest_end(vendor_event, wmmStats);
}
nla_nest_end(vendor_event, wmmInfo);
vos_mem_free(pWifiIfaceStatTL);
return TRUE;
}
static tSirWifiInterfaceMode
hdd_map_device_to_ll_iface_mode ( int deviceMode )
{
switch (deviceMode)
{
case WLAN_HDD_INFRA_STATION:
return WIFI_INTERFACE_STA;
case WLAN_HDD_SOFTAP:
return WIFI_INTERFACE_SOFTAP;
case WLAN_HDD_P2P_CLIENT:
return WIFI_INTERFACE_P2P_CLIENT;
case WLAN_HDD_P2P_GO:
return WIFI_INTERFACE_P2P_GO;
case WLAN_HDD_IBSS:
return WIFI_INTERFACE_IBSS;
default:
return WIFI_INTERFACE_UNKNOWN;
}
}
static v_BOOL_t hdd_get_interface_info(hdd_adapter_t *pAdapter,
tpSirWifiInterfaceInfo pInfo)
{
v_U8_t *staMac = NULL;
hdd_station_ctx_t *pHddStaCtx;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
pInfo->mode = hdd_map_device_to_ll_iface_mode(pAdapter->device_mode);
vos_mem_copy(pInfo->macAddr,
pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)))
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_NotConnected == pHddStaCtx->conn_info.connState)
{
pInfo->state = WIFI_DISCONNECTED;
}
if (eConnectionState_Connecting == pHddStaCtx->conn_info.connState)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Session ID %d, Connection is in progress", __func__,
pAdapter->sessionId);
pInfo->state = WIFI_ASSOCIATING;
}
if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
(VOS_FALSE == pHddStaCtx->conn_info.uIsAuthenticated))
{
staMac = (v_U8_t *) &(pAdapter->macAddressCurrent.bytes[0]);
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: client " MAC_ADDRESS_STR
" is in the middle of WPS/EAPOL exchange.", __func__,
MAC_ADDR_ARRAY(staMac));
pInfo->state = WIFI_AUTHENTICATING;
}
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
{
pInfo->state = WIFI_ASSOCIATED;
vos_mem_copy(pInfo->bssid,
&pHddStaCtx->conn_info.bssId, WNI_CFG_BSSID_LEN);
vos_mem_copy(pInfo->ssid,
pHddStaCtx->conn_info.SSID.SSID.ssId,
pHddStaCtx->conn_info.SSID.SSID.length);
//NULL Terminate the string.
pInfo->ssid[pHddStaCtx->conn_info.SSID.SSID.length] = 0;
}
}
vos_mem_copy(pInfo->countryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
vos_mem_copy(pInfo->apCountryStr,
pMac->scan.countryCodeCurrent, WNI_CFG_COUNTRY_CODE_LEN);
return TRUE;
}
/*
* hdd_link_layer_process_peer_stats () - This function is called after
* receiving Link Layer Peer statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static v_VOID_t hdd_link_layer_process_peer_stats(hdd_adapter_t *pAdapter,
v_VOID_t *pData)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tpSirWifiPeerStat pWifiPeerStat;
tpSirWifiPeerInfo pWifiPeerInfo;
struct nlattr *peerInfo;
struct sk_buff *vendor_event;
int status, i;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return;
}
pWifiPeerStat = (tpSirWifiPeerStat) pData;
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_PEER_ALL : numPeers %u",
pWifiPeerStat->numPeers);
/*
* Allocate a size of 4096 for the peer stats comprising
* each of size = sizeof (tSirWifiPeerInfo) + numRate *
* sizeof (tSirWifiRateStat).Each field is put with an
* NL attribute.The size of 4096 is considered assuming
* that number of rates shall not exceed beyond 50 with
* the sizeof (tSirWifiRateStat) being 32.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_cmd_alloc_reply_skb failed",
__func__);
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_PEER) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS,
pWifiPeerStat->numPeers))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR put fail", __func__);
kfree_skb(vendor_event);
return;
}
peerInfo = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO);
if(!peerInfo)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO put fail",
__func__);
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo) ((uint8 *)
pWifiPeerStat->peerInfo);
for (i = 1; i <= pWifiPeerStat->numPeers; i++)
{
int numRate = pWifiPeerInfo->numRate;
struct nlattr *peers = nla_nest_start(vendor_event, i);
if(!peers)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: peer stats put fail",
__func__);
kfree_skb(vendor_event);
return;
}
if (FALSE == put_wifi_peer_info(
pWifiPeerInfo, vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: put_wifi_peer_info put fail", __func__);
kfree_skb(vendor_event);
return;
}
pWifiPeerInfo = (tpSirWifiPeerInfo)((uint8 *)pWifiPeerInfo +
(sizeof(tSirWifiPeerInfo) - sizeof(tSirWifiRateStat)) +
(numRate * sizeof(tSirWifiRateStat)));
nla_nest_end(vendor_event, peers);
}
nla_nest_end(vendor_event, peerInfo);
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
}
/*
* hdd_link_layer_process_iface_stats () - This function is called after
* receiving Link Layer Interface statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static v_VOID_t hdd_link_layer_process_iface_stats(hdd_adapter_t *pAdapter,
v_VOID_t *pData)
{
tpSirWifiIfaceStat pWifiIfaceStat;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return;
}
/*
* Allocate a size of 4096 for the interface stats comprising
* sizeof (tpSirWifiIfaceStat).The size of 4096 is considered
* assuming that all these fit with in the limit.Please take
* a call on the limit based on the data requirements on
* interface statistics.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed") );
return;
}
pWifiIfaceStat = (tpSirWifiIfaceStat) pData;
if (FALSE == hdd_get_interface_info( pAdapter,
&pWifiIfaceStat->info))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("hdd_get_interface_info get fail") );
kfree_skb(vendor_event);
return;
}
if (FALSE == put_wifi_iface_stats( pAdapter, pWifiIfaceStat,
vendor_event))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("put_wifi_iface_stats fail") );
kfree_skb(vendor_event);
return;
}
hddLog(VOS_TRACE_LEVEL_INFO,
"WMI_LINK_STATS_IFACE Data");
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
}
/*
* hdd_link_layer_process_radio_stats () - This function is called after
* receiving Link Layer Radio statistics from FW.This function converts
* the firmware data to the NL data and sends the same to the kernel/upper
* layers.
*/
static v_VOID_t hdd_link_layer_process_radio_stats(hdd_adapter_t *pAdapter,
v_VOID_t *pData)
{
int status, i;
tpSirWifiRadioStat pWifiRadioStat;
tpSirWifiChannelStats pWifiChannelStats;
struct sk_buff *vendor_event;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
struct nlattr *chList;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return;
}
pWifiRadioStat = (tpSirWifiRadioStat) pData;
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_RADIO"
" number of radios = %u"
" radio is %d onTime is %u "
" txTime is %u rxTime is %u "
" onTimeScan is %u onTimeNbd is %u "
" onTimeEXTScan is %u onTimeRoamScan is %u "
" onTimePnoScan is %u onTimeHs20 is %u "
" numChannels is %u",
NUM_RADIOS,
pWifiRadioStat->radio, pWifiRadioStat->onTime,
pWifiRadioStat->txTime, pWifiRadioStat->rxTime,
pWifiRadioStat->onTimeScan, pWifiRadioStat->onTimeNbd,
pWifiRadioStat->onTimeEXTScan,
pWifiRadioStat->onTimeRoamScan,
pWifiRadioStat->onTimePnoScan,
pWifiRadioStat->onTimeHs20,
pWifiRadioStat->numChannels);
/*
* Allocate a size of 4096 for the Radio stats comprising
* sizeof (tSirWifiRadioStat) + numChannels * sizeof
* (tSirWifiChannelStats).Each channel data is put with an
* NL attribute.The size of 4096 is considered assuming that
* number of channels shall not exceed beyond 60 with the
* sizeof (tSirWifiChannelStats) being 24 bytes.
*/
vendor_event = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
LL_STATS_EVENT_BUF_SIZE);
if (!vendor_event)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed") );
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE_RADIO) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID,
pWifiRadioStat->radio) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS,
NUM_RADIOS) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME,
pWifiRadioStat->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME,
pWifiRadioStat->txTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME,
pWifiRadioStat->rxTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN,
pWifiRadioStat->onTimeScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD,
pWifiRadioStat->onTimeNbd) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_EXTSCAN,
pWifiRadioStat->onTimeEXTScan)||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN,
pWifiRadioStat->onTimeRoamScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN,
pWifiRadioStat->onTimePnoScan) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20,
pWifiRadioStat->onTimeHs20) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS,
pWifiRadioStat->numChannels))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("QCA_WLAN_VENDOR_ATTR put fail"));
kfree_skb(vendor_event);
return ;
}
chList = nla_nest_start(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO);
if(!chList)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO put fail",
__func__);
kfree_skb(vendor_event);
return;
}
for (i = 0; i < pWifiRadioStat->numChannels; i++)
{
struct nlattr *chInfo;
pWifiChannelStats = (tpSirWifiChannelStats) ((uint8*)
pWifiRadioStat->channels +
(i * sizeof(tSirWifiChannelStats)));
chInfo = nla_nest_start(vendor_event, i);
if(!chInfo)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to put chInfo",
__func__);
kfree_skb(vendor_event);
return;
}
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH,
pWifiChannelStats->channel.width) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ,
pWifiChannelStats->channel.centerFreq) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0,
pWifiChannelStats->channel.centerFreq0) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1,
pWifiChannelStats->channel.centerFreq1) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME,
pWifiChannelStats->onTime) ||
nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME,
pWifiChannelStats->ccaBusyTime))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed") );
kfree_skb(vendor_event);
return ;
}
nla_nest_end(vendor_event, chInfo);
}
nla_nest_end(vendor_event, chList);
cfg80211_vendor_cmd_reply(vendor_event);
EXIT();
return;
}
/*
* hdd_link_layer_stats_ind_callback () - This function is called after
* receiving Link Layer indications from FW.This callback converts the firmware
* data to the NL data and send the same to the kernel/upper layers.
*/
static void hdd_link_layer_stats_ind_callback ( void *pCtx,
int indType,
void *pRsp, u8 *macAddr)
{
hdd_context_t *pHddCtx = (hdd_context_t *)pCtx;
hdd_adapter_t *pAdapter = NULL;
struct hdd_ll_stats_context *context;
tpSirLLStatsResults linkLayerStatsResults = (tpSirLLStatsResults)pRsp;
int status;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return;
}
pAdapter = hdd_get_adapter_by_macaddr(pHddCtx, macAddr);
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL(" MAC address %pM does not exist with host"),
macAddr);
return;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Interface: %s LLStats indType: %d", __func__,
pAdapter->dev->name, indType);
switch (indType)
{
case SIR_HAL_LL_STATS_RESULTS_RSP:
{
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS RESP paramID = 0x%x, ifaceId = %u MAC: %pM "
"respId = %u, moreResultToFollow = %u",
linkLayerStatsResults->paramId, linkLayerStatsResults->ifaceId,
macAddr, linkLayerStatsResults->respId,
linkLayerStatsResults->moreResultToFollow);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ll_stats_context;
/* validate response received from target */
if ((context->request_id != linkLayerStatsResults->respId) ||
!(context->request_bitmap & linkLayerStatsResults->paramId))
{
spin_unlock(&hdd_context_lock);
hddLog(LOGE,
FL("Error : Request id %d response id %d request bitmap 0x%x"
"response bitmap 0x%x"),
context->request_id, linkLayerStatsResults->respId,
context->request_bitmap, linkLayerStatsResults->paramId);
return;
}
spin_unlock(&hdd_context_lock);
if ( linkLayerStatsResults->paramId & WMI_LINK_STATS_RADIO )
{
hdd_link_layer_process_radio_stats(pAdapter,
(v_VOID_t *)linkLayerStatsResults->result);
spin_lock(&hdd_context_lock);
context->request_bitmap &= ~(WMI_LINK_STATS_RADIO);
spin_unlock(&hdd_context_lock);
}
else if ( linkLayerStatsResults->paramId & WMI_LINK_STATS_IFACE )
{
hdd_link_layer_process_iface_stats(pAdapter,
(v_VOID_t *)linkLayerStatsResults->result);
spin_lock(&hdd_context_lock);
context->request_bitmap &= ~(WMI_LINK_STATS_IFACE);
spin_unlock(&hdd_context_lock);
}
else if ( linkLayerStatsResults->paramId &
WMI_LINK_STATS_ALL_PEER )
{
hdd_link_layer_process_peer_stats(pAdapter,
(v_VOID_t *)linkLayerStatsResults->result);
spin_lock(&hdd_context_lock);
context->request_bitmap &= ~(WMI_LINK_STATS_ALL_PEER);
spin_unlock(&hdd_context_lock);
} /* WMI_LINK_STATS_ALL_PEER */
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("INVALID LL_STATS_NOTIFY RESPONSE ***********"));
}
spin_lock(&hdd_context_lock);
/* complete response event if all requests are completed */
if (0 == context->request_bitmap)
complete(&context->response_event);
spin_unlock(&hdd_context_lock);
break;
}
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "invalid event type %d", indType);
break;
}
EXIT();
return;
}
const struct
nla_policy
qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING] =
{ .type = NLA_U32 },
};
static int __wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int status;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1];
tSirLLStatsSetReq linkLayerStatsSetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return -EINVAL;
}
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("HDD adapter is Null"));
return -ENODEV;
}
if (pAdapter->device_mode != WLAN_HDD_INFRA_STATION) {
hddLog(VOS_TRACE_LEVEL_DEBUG,
"Cannot set LL_STATS for device mode %d",
pAdapter->device_mode);
return -EINVAL;
}
/* check the LLStats Capability */
if ( (TRUE != pHddCtx->cfg_ini->fEnableLLStats) ||
(TRUE != sme_IsFeatureSupportedByFW(LINK_LAYER_STATS_MEAS)))
{
hddLog(VOS_TRACE_LEVEL_WARN,
FL("Link Layer Statistics not supported by Firmware"));
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_set_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("MPDU size Not present"));
return -EINVAL;
}
if (!tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL(" Stats Gathering Not Present"));
return -EINVAL;
}
// Shall take the request Id if the Upper layers pass. 1 For now.
linkLayerStatsSetReq.reqId = 1;
linkLayerStatsSetReq.mpduSizeThreshold =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD]);
linkLayerStatsSetReq.aggressiveStatisticsGathering =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING]);
vos_mem_copy(linkLayerStatsSetReq.macAddr,
pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_SET reqId = %d, MAC = %pM, mpduSizeThreshold = %d "
"Statistics Gathering = %d ",
linkLayerStatsSetReq.reqId, linkLayerStatsSetReq.macAddr,
linkLayerStatsSetReq.mpduSizeThreshold,
linkLayerStatsSetReq.aggressiveStatisticsGathering);
if (eHAL_STATUS_SUCCESS != sme_SetLinkLayerStatsIndCB(
pHddCtx->hHal,
hdd_link_layer_stats_ind_callback))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"sme_SetLinkLayerStatsIndCB Failed", __func__);
return -EINVAL;
}
if (eHAL_STATUS_SUCCESS != sme_LLStatsSetReq( pHddCtx->hHal,
&linkLayerStatsSetReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"sme_LLStatsSetReq Failed", __func__);
return -EINVAL;
}
pAdapter->isLinkLayerStatsSet = 1;
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_ll_stats_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_set(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_get_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX +1] =
{
/* Unsigned 32bit value provided by the caller issuing the GET stats
* command. When reporting
* the stats results, the driver uses the same value to indicate
* which GET request the results
* correspond to.
*/
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID] = { .type = NLA_U32 },
/* Unsigned 32bit value . bit mask to identify what statistics are
requested for retrieval */
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK] = { .type = NLA_U32 },
};
static int __wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
unsigned long rc;
struct hdd_ll_stats_context *context;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX + 1];
tSirLLStatsGetReq linkLayerStatsGetReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
int status;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return -EINVAL ;
}
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
if (pHddStaCtx == NULL)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: HddStaCtx is Null", __func__);
return -ENODEV;
}
/* check the LLStats Capability */
if ( (TRUE != pHddCtx->cfg_ini->fEnableLLStats) ||
(TRUE != sme_IsFeatureSupportedByFW(LINK_LAYER_STATS_MEAS)))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Link Layer Statistics not supported by Firmware"));
return -EINVAL;
}
if (!pAdapter->isLinkLayerStatsSet)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: isLinkLayerStatsSet : %d",
__func__, pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (VOS_TRUE == pHddStaCtx->hdd_ReassocScenario)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Roaming in progress, so unable to proceed this request", __func__);
return -EBUSY;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_get_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request Id Not present"));
return -EINVAL;
}
if (!tb_vendor
[QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Req Mask Not present"));
return -EINVAL;
}
linkLayerStatsGetReq.reqId =
nla_get_u32( tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID]);
linkLayerStatsGetReq.paramIdMask =
nla_get_u32( tb_vendor[
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK]);
vos_mem_copy(linkLayerStatsGetReq.macAddr,
pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_GET reqId = %d, MAC = %pM, paramIdMask = %d",
linkLayerStatsGetReq.reqId, linkLayerStatsGetReq.macAddr,
linkLayerStatsGetReq.paramIdMask);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ll_stats_context;
context->request_id = linkLayerStatsGetReq.reqId;
context->request_bitmap = linkLayerStatsGetReq.paramIdMask;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
if (eHAL_STATUS_SUCCESS != sme_LLStatsGetReq( pHddCtx->hHal,
&linkLayerStatsGetReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"sme_LLStatsGetReq Failed", __func__);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_LL_STATS));
if (!rc)
{
hddLog(LOGE,
FL("Target response timed out request id %d request bitmap 0x%x"),
context->request_id, context->request_bitmap);
return -ETIMEDOUT;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_ll_stats_get(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_get(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct
nla_policy
qca_wlan_vendor_ll_clr_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ] = {.type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK] = {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP] = {.type = NLA_U8 },
};
static int __wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX + 1];
tSirLLStatsClearReq linkLayerStatsClearReq;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
u32 statsClearReqMask;
u8 stopReq;
int status;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return -EINVAL;
}
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
/* check the LLStats Capability */
if ( (TRUE != pHddCtx->cfg_ini->fEnableLLStats) ||
(TRUE != sme_IsFeatureSupportedByFW(LINK_LAYER_STATS_MEAS)))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Enable LLStats Capability"));
return -EINVAL;
}
if (!pAdapter->isLinkLayerStatsSet)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: isLinkLayerStatsSet : %d",
__func__, pAdapter->isLinkLayerStatsSet);
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX,
(struct nlattr *)data,
data_len, qca_wlan_vendor_ll_clr_policy))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL() );
return -EINVAL;
}
if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK] ||
!tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Error in LL_STATS CLR CONFIG PARA") );
return -EINVAL;
}
statsClearReqMask = linkLayerStatsClearReq.statsClearReqMask =
nla_get_u32(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK]);
stopReq = linkLayerStatsClearReq.stopReq =
nla_get_u8(
tb_vendor[QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ]);
// Shall take the request Id if the Upper layers pass. 1 For now.
linkLayerStatsClearReq.reqId = 1;
vos_mem_copy(linkLayerStatsClearReq.macAddr,
pAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
hddLog(VOS_TRACE_LEVEL_INFO,
"LL_STATS_CLEAR reqId = %d, MAC = %pM,"
"statsClearReqMask = 0x%X, stopReq = %d",
linkLayerStatsClearReq.reqId,
linkLayerStatsClearReq.macAddr,
linkLayerStatsClearReq.statsClearReqMask,
linkLayerStatsClearReq.stopReq);
if (eHAL_STATUS_SUCCESS == sme_LLStatsClearReq(pHddCtx->hHal,
&linkLayerStatsClearReq))
{
struct sk_buff *temp_skbuff;
hdd_station_ctx_t *pHddStaCtx;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (VOS_STATUS_SUCCESS !=
WLANTL_ClearInterfaceStats((WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
pHddStaCtx->conn_info.staId[0], statsClearReqMask))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:"
"WLANTL_ClearInterfaceStats Failed", __func__);
return -EINVAL;
}
if ((statsClearReqMask & WIFI_STATS_IFACE_AC) ||
(statsClearReqMask & WIFI_STATS_IFACE)) {
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_VO] = 0;
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_VI] = 0;
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_BE] = 0;
pAdapter->hdd_stats.hddTxRxStats.txMcast[WLANTL_AC_BK] = 0;
}
temp_skbuff = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
2 * sizeof(u32) +
NLMSG_HDRLEN);
if (temp_skbuff != NULL)
{
if (nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK,
statsClearReqMask) ||
nla_put_u32(temp_skbuff,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP,
stopReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("LL_STATS_CLR put fail"));
kfree_skb(temp_skbuff);
return -EINVAL;
}
/* If the ask is to stop the stats collection as part of clear
* (stopReq = 1) , ensure that no further requests of get
* go to the firmware by having isLinkLayerStatsSet set to 0.
* However it the stopReq as part of the clear request is 0 ,
* the request to get the statistics are honoured as in this
* case the firmware is just asked to clear the statistics.
*/
if (linkLayerStatsClearReq.stopReq == 1)
pAdapter->isLinkLayerStatsSet = 0;
return cfg80211_vendor_cmd_reply(temp_skbuff);
}
return -ENOMEM;
}
EXIT();
return -EINVAL;
}
static int wlan_hdd_cfg80211_ll_stats_clear(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_ll_stats_clear(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
#ifdef WLAN_FEATURE_EXTSCAN
static const struct nla_policy
wlan_hdd_extscan_config_policy
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1] =
{
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CLASS] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND] = { .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_EXPONENT] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW] =
{ .type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH] =
{ .type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_CHANNEL] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_SSID] =
{ .type = NLA_BINARY,
.len = IEEE80211_MAX_SSID_LEN + 1 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_HOTLIST_PARAMS_NUM_SSID] =
{ .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_BAND] =
{ .type = NLA_U8 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW] =
{ .type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH] =
{ .type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CONFIGURATION_FLAGS] =
{ .type = NLA_U32 },
};
/**
* wlan_hdd_cfg80211_extscan_get_capabilities_rsp() - response from target
* @ctx: hdd global context
* @data: capabilities data
*
* Return: none
*/
static void
wlan_hdd_cfg80211_extscan_get_capabilities_rsp(void *ctx, void *pMsg)
{
struct hdd_ext_scan_context *context;
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
tSirEXTScanCapabilitiesEvent *data =
(tSirEXTScanCapabilitiesEvent *) pMsg;
ENTER();
if (wlan_hdd_validate_context(pHddCtx))
{
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
vos_spin_lock_acquire(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
/* validate response received from target*/
if (context->request_id != data->requestId)
{
vos_spin_lock_release(&hdd_context_lock);
hddLog(LOGE,
FL("Target response id did not match: request_id %d resposne_id %d"),
context->request_id, data->requestId);
return;
}
else
{
context->capability_response = *data;
complete(&context->response_event);
}
vos_spin_lock_release(&hdd_context_lock);
return;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_send_ext_scan_capability()
*/
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
#define PARAM_STATUS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_STATUS
#define MAX_SCAN_CACHE_SIZE \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE
#define MAX_SCAN_BUCKETS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS
#define MAX_AP_CACHE_PER_SCAN \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
#define MAX_RSSI_SAMPLE_SIZE \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
#define MAX_SCAN_RPT_THRHOLD \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
#define MAX_HOTLIST_BSSIDS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS
#define MAX_BSSID_HISTORY_ENTRIES \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
#define MAX_HOTLIST_SSIDS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS
#define MAX_SIGNIFICANT_WIFI_CHANGE_APS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
static int wlan_hdd_send_ext_scan_capability(void *ctx)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *skb = NULL;
int ret;
tSirEXTScanCapabilitiesEvent *data;
tANI_U32 nl_buf_len;
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
data = &(pHddCtx->ext_scan_context.capability_response);
nl_buf_len = NLMSG_HDRLEN;
nl_buf_len += (sizeof(data->requestId) + NLA_HDRLEN) +
(sizeof(data->status) + NLA_HDRLEN) +
(sizeof(data->scanCacheSize) + NLA_HDRLEN) +
(sizeof(data->scanBuckets) + NLA_HDRLEN) +
(sizeof(data->maxApPerScan) + NLA_HDRLEN) +
(sizeof(data->maxRssiSampleSize) + NLA_HDRLEN) +
(sizeof(data->maxScanReportingThreshold) + NLA_HDRLEN) +
(sizeof(data->maxHotlistAPs) + NLA_HDRLEN) +
(sizeof(data->maxBsidHistoryEntries) + NLA_HDRLEN) +
(sizeof(data->maxHotlistSSIDs) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy, nl_buf_len);
if (!skb)
{
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, "Req Id (%u) Status (%u)", data->requestId, data->status);
hddLog(LOG1, "Scan cache size (%u) Scan buckets (%u) Max AP per scan (%u)",
data->scanCacheSize, data->scanBuckets, data->maxApPerScan);
hddLog(LOG1, "max_rssi_sample_size (%u) max_scan_reporting_threshold (%u)",
data->maxRssiSampleSize, data->maxScanReportingThreshold);
hddLog(LOG1, "max_hotlist_bssids (%u) max_bssid_history_entries (%u)"
"max_hotlist_ssids (%u)", data->maxHotlistAPs,
data->maxBsidHistoryEntries, data->maxHotlistSSIDs);
if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) ||
nla_put_u32(skb, PARAM_STATUS, data->status) ||
nla_put_u32(skb, MAX_SCAN_CACHE_SIZE, data->scanCacheSize) ||
nla_put_u32(skb, MAX_SCAN_BUCKETS, data->scanBuckets) ||
nla_put_u32(skb, MAX_AP_CACHE_PER_SCAN,
data->maxApPerScan) ||
nla_put_u32(skb, MAX_RSSI_SAMPLE_SIZE,
data->maxRssiSampleSize) ||
nla_put_u32(skb, MAX_SCAN_RPT_THRHOLD,
data->maxScanReportingThreshold) ||
nla_put_u32(skb, MAX_HOTLIST_BSSIDS, data->maxHotlistAPs) ||
nla_put_u32(skb, MAX_BSSID_HISTORY_ENTRIES,
data->maxBsidHistoryEntries) ||
nla_put_u32(skb, MAX_HOTLIST_SSIDS, data->maxHotlistSSIDs) ||
nla_put_u32(skb, MAX_SIGNIFICANT_WIFI_CHANGE_APS, 0))
{
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_cmd_reply(skb);
return 0;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_send_ext_scan_capability()
*/
#undef PARAM_REQUEST_ID
#undef PARAM_STATUS
#undef MAX_SCAN_CACHE_SIZE
#undef MAX_SCAN_BUCKETS
#undef MAX_AP_CACHE_PER_SCAN
#undef MAX_RSSI_SAMPLE_SIZE
#undef MAX_SCAN_RPT_THRHOLD
#undef MAX_HOTLIST_BSSIDS
#undef MAX_BSSID_HISTORY_ENTRIES
#undef MAX_HOTLIST_SSIDS
static void wlan_hdd_cfg80211_extscan_start_rsp(void *ctx, void *pMsg)
{
tpSirEXTScanStartRspParams pData = (tpSirEXTScanStartRspParams) pMsg;
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
tpAniSirGlobal pMac = PMAC_STRUCT( pHddCtx->hHal );
struct hdd_ext_scan_context *context;
ENTER();
if (wlan_hdd_validate_context(pHddCtx))
return;
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id (%u)", pData->requestId);
hddLog(VOS_TRACE_LEVEL_INFO, "Status (%u)", pData->status);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
if (context->request_id == pData->requestId) {
context->response_status = pData->status ? -EINVAL : 0;
complete(&context->response_event);
}
spin_unlock(&hdd_context_lock);
/*
* Store the Request ID for comparing with the requestID obtained
* in other requests.HDD shall return a failure is the extscan_stop
* request is issued with a different requestId as that of the
* extscan_start request. Also, This requestId shall be used while
* indicating the full scan results to the upper layers.
* The requestId is stored with the assumption that the firmware
* shall return the ext scan start request's requestId in ext scan
* start response.
*/
if (pData->status == 0)
pMac->sme.extScanStartReqId = pData->requestId;
EXIT();
return;
}
static void wlan_hdd_cfg80211_extscan_stop_rsp(void *ctx, void *pMsg)
{
tpSirEXTScanStopRspParams pData = (tpSirEXTScanStopRspParams) pMsg;
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct hdd_ext_scan_context *context;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)){
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id %u Status %u", pData->requestId,
pData->status);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
if (context->request_id == pData->requestId) {
context->response_status = pData->status ? -EINVAL : 0;
complete(&context->response_event);
}
spin_unlock(&hdd_context_lock);
EXIT();
return;
}
static void wlan_hdd_cfg80211_extscan_set_bss_hotlist_rsp(void *ctx,
void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
tpSirEXTScanSetBssidHotListRspParams pData =
(tpSirEXTScanSetBssidHotListRspParams) pMsg;
struct hdd_ext_scan_context *context;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)){
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id %u Status %u", pData->requestId,
pData->status);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
if (context->request_id == pData->requestId) {
context->response_status = pData->status ? -EINVAL : 0;
complete(&context->response_event);
}
spin_unlock(&hdd_context_lock);
EXIT();
return;
}
static void wlan_hdd_cfg80211_extscan_reset_bss_hotlist_rsp(void *ctx,
void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
tpSirEXTScanResetBssidHotlistRspParams pData =
(tpSirEXTScanResetBssidHotlistRspParams) pMsg;
struct hdd_ext_scan_context *context;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id %u Status %u", pData->requestId,
pData->status);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
if (context->request_id == pData->requestId) {
context->response_status = pData->status ? -EINVAL : 0;
complete(&context->response_event);
}
spin_unlock(&hdd_context_lock);
EXIT();
return;
}
static void wlan_hdd_cfg80211_extscan_cached_results_ind(void *ctx,
void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *skb = NULL;
tANI_U32 i = 0, j, resultsPerEvent, scan_id_index;
tANI_S32 totalResults;
tpSirWifiScanResultEvent pData = (tpSirWifiScanResultEvent) pMsg;
tpSirWifiScanResult pSirWifiScanResult, head_ptr;
struct hdd_ext_scan_context *context;
bool ignore_cached_results = false;
tExtscanCachedScanResult *result;
struct nlattr *nla_results;
tANI_U16 ieLength= 0;
tANI_U8 *ie = NULL;
ENTER();
if (wlan_hdd_validate_context(pHddCtx))
return;
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
spin_lock(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
ignore_cached_results = context->ignore_cached_results;
spin_unlock(&hdd_context_lock);
if (ignore_cached_results) {
hddLog(LOGE,
FL("Ignore the cached results received after timeout"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id %u More Data %u No of scan ids %u",
pData->requestId, pData->moreData, pData->scanResultSize);
result = (tExtscanCachedScanResult *)&(pData->result);
for (scan_id_index = 0; scan_id_index < pData->scanResultSize;
scan_id_index++) {
result+= scan_id_index;
totalResults = result->num_results;
hddLog(VOS_TRACE_LEVEL_INFO, "scan_id %u flags %u Num results %u",
result->scan_id, result->flags, totalResults);
i = 0;
do{
resultsPerEvent = ((totalResults >= EXTSCAN_MAX_CACHED_RESULTS_PER_IND) ?
EXTSCAN_MAX_CACHED_RESULTS_PER_IND : totalResults);
totalResults -= EXTSCAN_MAX_CACHED_RESULTS_PER_IND;
skb = cfg80211_vendor_cmd_alloc_reply_skb(pHddCtx->wiphy,
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "resultsPerEvent (%u)", resultsPerEvent);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID,
pData->requestId) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE,
resultsPerEvent)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u8(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA,
pData->moreData ? 1 : (totalResults > 0 ? 1 : 0 )))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID,
result->scan_id)) {
hddLog(LOGE, FL("put fail"));
goto fail;
}
nla_results = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_LIST);
if (!nla_results)
goto fail;
if (resultsPerEvent) {
struct nlattr *aps;
struct nlattr *nla_result;
nla_result = nla_nest_start(skb, scan_id_index);
if(!nla_result)
goto fail;
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_SCAN_ID,
result->scan_id) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CACHED_RESULTS_FLAGS,
result->flags) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE,
totalResults)) {
hddLog(LOGE, FL("put fail"));
goto fail;
}
aps = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST);
if (!aps)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
head_ptr = (tpSirWifiScanResult) &(result->ap);
for (j = 0; j < resultsPerEvent; j++, i++) {
struct nlattr *ap;
pSirWifiScanResult = head_ptr + i;
/*
* Firmware returns timestamp from extscan_start till
* BSSID was cached (in micro seconds). Add this with
* time gap between system boot up to extscan_start
* to derive the time since boot when the
* BSSID was cached.
*/
pSirWifiScanResult->ts +=
pHddCtx->extscan_start_time_since_boot;
hddLog(VOS_TRACE_LEVEL_INFO, "[index=%u] Timestamp(%llu) "
"Ssid (%s)"
"Bssid: %pM "
"Channel (%u)"
"Rssi (%d)"
"RTT (%u)"
"RTT_SD (%u)"
"Beacon Period %u"
"Capability 0x%x "
"Ie length %d",
i,
pSirWifiScanResult->ts,
pSirWifiScanResult->ssid,
pSirWifiScanResult->bssid,
pSirWifiScanResult->channel,
pSirWifiScanResult->rssi,
pSirWifiScanResult->rtt,
pSirWifiScanResult->rtt_sd,
pSirWifiScanResult->beaconPeriod,
pSirWifiScanResult->capability,
ieLength);
ap = nla_nest_start(skb, j + 1);
if (!ap)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (hdd_wlan_nla_put_u64(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP,
pSirWifiScanResult->ts) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID,
sizeof(pSirWifiScanResult->ssid),
pSirWifiScanResult->ssid) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID,
sizeof(pSirWifiScanResult->bssid),
pSirWifiScanResult->bssid) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL,
pSirWifiScanResult->channel) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI,
pSirWifiScanResult->rssi) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT,
pSirWifiScanResult->rtt) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD,
pSirWifiScanResult->rtt_sd))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD,
pSirWifiScanResult->beaconPeriod))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY,
pSirWifiScanResult->capability))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH,
ieLength))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (ieLength)
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA,
ieLength, ie)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
nla_nest_end(skb, ap);
}
nla_nest_end(skb, aps);
nla_nest_end(skb, nla_result);
}
nla_nest_end(skb, nla_results);
cfg80211_vendor_cmd_reply(skb);
} while (totalResults > 0);
}
if (!pData->moreData) {
spin_lock(&hdd_context_lock);
context->response_status = 0;
complete(&context->response_event);
spin_unlock(&hdd_context_lock);
}
EXIT();
return;
fail:
kfree_skb(skb);
return;
}
static void wlan_hdd_cfg80211_extscan_hotlist_match_ind(void *ctx,
void *pMsg)
{
tpSirEXTScanHotlistMatch pData = (tpSirEXTScanHotlistMatch) pMsg;
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *skb = NULL;
tANI_U32 i, index;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
hddLog(LOGE,
FL("HDD context is not valid or response"));
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
if (pData->bss_found)
index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND_INDEX;
else
index = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX;
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
index, GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id (%u)", pData->requestId);
hddLog(VOS_TRACE_LEVEL_INFO, "Num results (%u)", pData->numHotlistBss);
hddLog(VOS_TRACE_LEVEL_INFO, "More Data (%u)", pData->moreData);
hddLog(VOS_TRACE_LEVEL_INFO, "ap_found %u", pData->bss_found);
for (i = 0; i < pData->numHotlistBss; i++) {
hddLog(VOS_TRACE_LEVEL_INFO, "[index=%u] Timestamp(0x%lld) "
"Ssid (%s) "
"Bssid (" MAC_ADDRESS_STR ") "
"Channel (%u) "
"Rssi (%d) "
"RTT (%u) "
"RTT_SD (%u) ",
i,
pData->bssHotlist[i].ts,
pData->bssHotlist[i].ssid,
MAC_ADDR_ARRAY(pData->bssHotlist[i].bssid),
pData->bssHotlist[i].channel,
pData->bssHotlist[i].rssi,
pData->bssHotlist[i].rtt,
pData->bssHotlist[i].rtt_sd);
}
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID,
pData->requestId) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE,
pData->numHotlistBss)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("put fail"));
goto fail;
}
if (pData->numHotlistBss) {
struct nlattr *aps;
aps = nla_nest_start(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_LIST);
if (!aps)
goto fail;
for (i = 0; i < pData->numHotlistBss; i++) {
struct nlattr *ap;
ap = nla_nest_start(skb, i + 1);
if (!ap)
goto fail;
if (hdd_wlan_nla_put_u64(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP,
pData->bssHotlist[i].ts) ||
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID,
sizeof(pData->bssHotlist[i].ssid),
pData->bssHotlist[i].ssid) ||
nla_put(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID,
sizeof(pData->bssHotlist[i].bssid),
pData->bssHotlist[i].bssid) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL,
pData->bssHotlist[i].channel) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI,
pData->bssHotlist[i].rssi) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT,
pData->bssHotlist[i].rtt) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD,
pData->bssHotlist[i].rtt_sd))
goto fail;
nla_nest_end(skb, ap);
}
nla_nest_end(skb, aps);
if (nla_put_u8(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA,
pData->moreData))
goto fail;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return;
fail:
kfree_skb(skb);
return;
}
static void wlan_hdd_cfg80211_extscan_full_scan_result_event(void *ctx,
void *pMsg)
{
struct sk_buff *skb;
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
tpSirWifiFullScanResultEvent pData =
(tpSirWifiFullScanResultEvent) (pMsg);
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
hddLog(LOGE,
FL("HDD context is not valid or response"));
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
/*
* If the full scan result including IE data exceeds NL 4K size
* limitation, drop that beacon/probe rsp frame.
*/
if ((sizeof(*pData) + pData->ieLength) >= EXTSCAN_EVENT_BUF_SIZE) {
hddLog(LOGE, FL("Frame exceeded NL size limilation, drop it!"));
return;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%u)"), pData->requestId);
hddLog(VOS_TRACE_LEVEL_INFO, FL("More Data (%u)"), pData->moreData);
hddLog(VOS_TRACE_LEVEL_INFO, FL("AP Info: Timestamp(0x%llX) "
"Ssid (%s)"
"Bssid (" MAC_ADDRESS_STR ")"
"Channel (%u)"
"Rssi (%d)"
"RTT (%u)"
"RTT_SD (%u)"
"Bcn Period %d"
"Capability 0x%X "),
pData->ap.ts,
pData->ap.ssid,
MAC_ADDR_ARRAY(pData->ap.bssid),
pData->ap.channel,
pData->ap.rssi,
pData->ap.rtt,
pData->ap.rtt_sd,
pData->ap.beaconPeriod,
pData->ap.capability);
hddLog(VOS_TRACE_LEVEL_INFO, "IE Length (%u)", pData->ieLength);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID,
pData->requestId) ||
hdd_wlan_nla_put_u64(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_TIME_STAMP,
pData->ap.ts) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_SSID,
sizeof(pData->ap.ssid),
pData->ap.ssid) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BSSID,
WNI_CFG_BSSID_LEN,
pData->ap.bssid) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CHANNEL,
pData->ap.channel) ||
nla_put_s32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RSSI,
pData->ap.rssi) ||
nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT,
pData->ap.rtt) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_RTT_SD,
pData->ap.rtt_sd) ||
nla_put_u16(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD,
pData->ap.beaconPeriod) ||
nla_put_u16(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_CAPABILITY,
pData->ap.capability) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_LENGTH,
pData->ieLength) ||
nla_put_u8(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_MORE_DATA,
pData->moreData))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
if (pData->ieLength) {
if (nla_put(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_RESULT_IE_DATA,
pData->ieLength,
pData->ie))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return;
nla_put_failure:
kfree_skb(skb);
return;
}
static void wlan_hdd_cfg80211_extscan_scan_res_available_event(void *ctx,
void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *skb = NULL;
tpSirEXTScanResultsAvailableIndParams pData =
(tpSirEXTScanResultsAvailableIndParams) pMsg;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)){
hddLog(LOGE,
FL("HDD context is not valid or response"));
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "Req Id (%u)", pData->requestId);
hddLog(VOS_TRACE_LEVEL_INFO, "Num results (%u)",
pData->numResultsAvailable);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID,
pData->requestId) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_RESULTS_AVAILABLE,
pData->numResultsAvailable)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return;
nla_put_failure:
kfree_skb(skb);
return;
}
static void wlan_hdd_cfg80211_extscan_scan_progress_event(void *ctx, void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
struct sk_buff *skb = NULL;
tpSirEXTScanProgressIndParams pData =
(tpSirEXTScanProgressIndParams) pMsg;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)){
hddLog(LOGE,
FL("HDD context is not valid or response"));
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Request Id (%u) "), pData->requestId);
hddLog(VOS_TRACE_LEVEL_INFO, "Scan event type (%u)",
pData->extScanEventType);
hddLog(VOS_TRACE_LEVEL_INFO, "Scan event status (%u)",
pData->status);
if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_EVENT_TYPE,
pData->extScanEventType) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_REQUEST_ID,
pData->requestId) ||
nla_put_u32(skb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_SCAN_EVENT_STATUS,
pData->status)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return;
nla_put_failure:
kfree_skb(skb);
return;
}
void wlan_hdd_cfg80211_extscan_callback(void *ctx, const tANI_U16 evType,
void *pMsg)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Rcvd Event (%d)"), evType);
switch(evType) {
case SIR_HAL_EXTSCAN_START_RSP:
wlan_hdd_cfg80211_extscan_start_rsp(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_STOP_RSP:
wlan_hdd_cfg80211_extscan_stop_rsp(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_GET_CACHED_RESULTS_RSP:
/* There is no need to send this response to upper layer
Just log the message */
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Rcvd SIR_HAL_EXTSCAN_CACHED_RESULTS_RSP"));
break;
case SIR_HAL_EXTSCAN_SET_BSS_HOTLIST_RSP:
wlan_hdd_cfg80211_extscan_set_bss_hotlist_rsp(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_RESET_BSS_HOTLIST_RSP:
wlan_hdd_cfg80211_extscan_reset_bss_hotlist_rsp(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_GET_CAPABILITIES_RSP:
wlan_hdd_cfg80211_extscan_get_capabilities_rsp(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_PROGRESS_IND:
wlan_hdd_cfg80211_extscan_scan_progress_event(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_SCAN_AVAILABLE_IND:
wlan_hdd_cfg80211_extscan_scan_res_available_event(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_SCAN_RESULT_IND:
wlan_hdd_cfg80211_extscan_cached_results_ind(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_HOTLIST_MATCH_IND:
wlan_hdd_cfg80211_extscan_hotlist_match_ind(ctx, pMsg);
break;
case SIR_HAL_EXTSCAN_FULL_SCAN_RESULT_IND:
wlan_hdd_cfg80211_extscan_full_scan_result_event(ctx, pMsg);
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, FL("invalid event type %d "), evType);
break;
}
EXIT();
}
static bool wlan_hdd_is_extscan_supported(hdd_adapter_t *adapter,
hdd_context_t *hdd_ctx)
{
int status;
status = wlan_hdd_validate_context(hdd_ctx);
if (status)
return false;
if (!adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid adapter"));
return false;
}
if (adapter->device_mode != WLAN_HDD_INFRA_STATION) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ext scans only supported on STA ifaces"));
return false;
}
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return false;
}
/* check the EXTScan Capability */
if ( (TRUE != hdd_ctx->cfg_ini->fEnableEXTScan) ||
(TRUE != sme_IsFeatureSupportedByFW(EXTENDED_SCAN)) ||
(TRUE != sme_IsFeatureSupportedByFW(EXT_SCAN_ENHANCED))) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("EXTScan not enabled/supported by Firmware"));
return false;
}
return true;
}
static int __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tSirGetEXTScanCapabilitiesReqParams reqMsg;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr
*tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
eHalStatus status;
struct hdd_ext_scan_context *context;
unsigned long rc;
int ret;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
reqMsg.requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), reqMsg.requestId);
reqMsg.sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), reqMsg.sessionId);
vos_spin_lock_acquire(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
context->request_id = reqMsg.requestId;
INIT_COMPLETION(context->response_event);
vos_spin_lock_release(&hdd_context_lock);
status = sme_EXTScanGetCapabilities(pHddCtx->hHal, &reqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_EXTScanGetCapabilities failed(err=%d)"), status);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
return -ETIMEDOUT;
}
ret = wlan_hdd_send_ext_scan_capability(pHddCtx);
if (ret)
hddLog(LOGE, FL("Failed to send ext scan capability to user space"));
return ret;
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_capabilities(wiphy, wdev, data, dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tSirEXTScanGetCachedResultsReqParams reqMsg;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr
*tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
eHalStatus status;
struct hdd_ext_scan_context *context;
unsigned long rc;
int retval;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
reqMsg.requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), reqMsg.requestId);
reqMsg.sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), reqMsg.sessionId);
/* Parse and fetch flush parameter */
if (!tb
[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr flush failed"));
goto failed;
}
reqMsg.flush = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Flush (%d)"), reqMsg.flush);
spin_lock(&hdd_context_lock);
context = &pHddCtx->ext_scan_context;
context->request_id = reqMsg.requestId;
context->ignore_cached_results = false;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
status = sme_getCachedResults(pHddCtx->hHal, &reqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_getCachedResults failed(err=%d)"), status);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("Target response timed out"));
retval = -ETIMEDOUT;
spin_lock(&hdd_context_lock);
context->ignore_cached_results = true;
spin_unlock(&hdd_context_lock);
} else {
spin_lock(&hdd_context_lock);
retval = context->response_status;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
failed:
return -EINVAL;
}
static int wlan_hdd_cfg80211_extscan_get_cached_results(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_cached_results(wiphy, wdev, data, dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tpSirEXTScanSetBssidHotListReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr
*tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr
*tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *apTh;
eHalStatus status;
tANI_U8 i = 0;
int rem;
struct hdd_ext_scan_context *context;
tANI_U32 request_id;
unsigned long rc;
int retval;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
pReqMsg = (tpSirEXTScanSetBssidHotListReqParams)
vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
pReqMsg->requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), pReqMsg->requestId);
/* Parse and fetch number of APs */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr number of AP failed"));
goto fail;
}
/* Parse and fetch lost ap sample size */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]) {
hddLog(LOGE, FL("attr lost ap sample size failed"));
goto fail;
}
pReqMsg->lostBssidSampleSize = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE]);
hddLog(LOG1, FL("Lost ap sample size %d"), pReqMsg->lostBssidSampleSize);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), pReqMsg->sessionId);
pReqMsg->numBssid = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BSSID_HOTLIST_PARAMS_NUM_AP]);
if (pReqMsg->numBssid > WLAN_EXTSCAN_MAX_HOTLIST_APS) {
hddLog(LOGE, FL("Number of AP: %u exceeds max: %u"),
pReqMsg->numBssid, WLAN_EXTSCAN_MAX_HOTLIST_APS);
goto fail;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Number of AP (%d)"), pReqMsg->numBssid);
nla_for_each_nested(apTh,
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM], rem) {
if (i == pReqMsg->numBssid) {
hddLog(LOGW, FL("Ignoring excess AP"));
break;
}
if(nla_parse(tb2, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(apTh), nla_len(apTh),
NULL)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_parse failed"));
goto fail;
}
/* Parse and fetch MAC address */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac address failed"));
goto fail;
}
memcpy(pReqMsg->ap[i].bssid, nla_data(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_BSSID]),
sizeof(tSirMacAddr));
hddLog(VOS_TRACE_LEVEL_INFO, FL("BSSID: %pM "), pReqMsg->ap[i].bssid);
/* Parse and fetch low RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr low RSSI failed"));
goto fail;
}
pReqMsg->ap[i].low = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_LOW]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("RSSI low (%d)"), pReqMsg->ap[i].low);
/* Parse and fetch high RSSI */
if (!tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr high RSSI failed"));
goto fail;
}
pReqMsg->ap[i].high = nla_get_s32(
tb2[QCA_WLAN_VENDOR_ATTR_EXTSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("RSSI High (%d)"),
pReqMsg->ap[i].high);
i++;
}
if (i < pReqMsg->numBssid) {
hddLog(LOGW, FL("Number of AP %u less than expected %u"),
i, pReqMsg->numBssid);
pReqMsg->numBssid = i;
}
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_SetBssHotlist(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_SetBssHotlist failed(err=%d)"), status);
vos_mem_free(pReqMsg);
return -EINVAL;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_SetBssHotlist timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
vos_mem_free(pReqMsg);
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
static int wlan_hdd_cfg80211_extscan_set_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_set_bssid_hotlist(wiphy, wdev, data,
dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
uint32_t chan_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
uint8_t num_channels = 0;
uint8_t num_chan_new = 0;
uint8_t buf[256] = {0};
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
tANI_U32 requestId, maxChannels;
tWifiBand wifiBand;
eHalStatus status;
struct sk_buff *replySkb;
tANI_U8 i,j,k;
int ret,len = 0;;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), requestId);
/* Parse and fetch wifi band */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND])
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr wifi band failed"));
return -EINVAL;
}
wifiBand = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Wifi band (%d)"), wifiBand);
/* Parse and fetch max channels */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS])
{
hddLog(LOGE, FL("attr max channels failed"));
return -EINVAL;
}
maxChannels = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Max channels %d"), maxChannels);
status = sme_GetValidChannelsByBand((tHalHandle)(pHddCtx->hHal),
wifiBand, chan_list,
&num_channels);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_GetValidChannelsByBand failed (err=%d)"), status);
return -EINVAL;
}
num_channels = VOS_MIN(num_channels, maxChannels);
num_chan_new = num_channels;
/* remove the indoor only channels if iface is SAP */
if (WLAN_HDD_SOFTAP == pAdapter->device_mode)
{
num_chan_new = 0;
for (i = 0; i < num_channels; i++)
for (j = 0; j < HDD_NUM_NL80211_BANDS; j++) {
if (wiphy->bands[j] == NULL)
continue;
for (k = 0; k < wiphy->bands[j]->n_channels; k++) {
if ((chan_list[i] ==
wiphy->bands[j]->channels[k].center_freq) &&
(!(wiphy->bands[j]->channels[k].flags &
IEEE80211_CHAN_INDOOR_ONLY))) {
chan_list[num_chan_new] = chan_list[i];
num_chan_new++;
}
}
}
}
hddLog(LOG1, FL("Number of channels: %d"), num_chan_new);
for (i = 0; i < num_chan_new; i++)
len += scnprintf(buf + len, sizeof(buf) - len, "%u ", chan_list[i]);
hddLog(LOG1, "Channels: %s", buf);
replySkb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
sizeof(u32) * num_chan_new +
NLMSG_HDRLEN);
if (!replySkb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("valid channels: buffer alloc fail"));
return -EINVAL;
}
if (nla_put_u32(replySkb,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_NUM_CHANNELS,
num_chan_new) ||
nla_put(replySkb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CHANNELS,
sizeof(u32) * num_chan_new, chan_list)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
kfree_skb(replySkb);
return -EINVAL;
}
ret = cfg80211_vendor_cmd_reply(replySkb);
EXIT();
return ret;
}
static int wlan_hdd_cfg80211_extscan_get_valid_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_get_valid_channels(wiphy, wdev, data,
dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int hdd_extscan_start_fill_bucket_channel_spec(
hdd_context_t *pHddCtx,
tpSirEXTScanStartReqParams pReqMsg,
struct nlattr **tb)
{
struct nlattr *bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
struct nlattr *buckets;
struct nlattr *channels;
int rem1, rem2;
eHalStatus status;
tANI_U8 bktIndex, j, numChannels;
uint32_t expected_buckets;
tANI_U32 chanList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U32 passive_max_chn_time, active_max_chn_time;
expected_buckets = pReqMsg->numBuckets;
bktIndex = 0;
nla_for_each_nested(buckets,
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC], rem1) {
if (bktIndex >= expected_buckets) {
hddLog(LOGW, FL("ignoring excess buckets"));
break;
}
if (nla_parse(bucket,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(buckets), nla_len(buckets),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch bucket spec */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]) {
hddLog(LOGE, FL("attr bucket index failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].bucket = nla_get_u8(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_INDEX]);
hddLog(LOG1, FL("Bucket spec Index %d"),
pReqMsg->buckets[bktIndex].bucket);
/* Parse and fetch wifi band */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]) {
hddLog(LOGE, FL("attr wifi band failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].band = nla_get_u8(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_BAND]);
hddLog(LOG1, FL("Wifi band %d"),
pReqMsg->buckets[bktIndex].band);
/* Parse and fetch period */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]) {
hddLog(LOGE, FL("attr period failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].period = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_PERIOD]);
hddLog(LOG1, FL("period %d"),
pReqMsg->buckets[bktIndex].period);
/* Parse and fetch report events */
if (!bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]) {
hddLog(LOGE, FL("attr report events failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].reportEvents = nla_get_u8(
bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_REPORT_EVENTS]);
hddLog(LOG1, FL("report events %d"),
pReqMsg->buckets[bktIndex].reportEvents);
/* Parse and fetch max period */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]) {
hddLog(LOGE, FL("attr max period failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].max_period = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_MAX_PERIOD]);
hddLog(LOG1, FL("max period %u"),
pReqMsg->buckets[bktIndex].max_period);
/* Parse and fetch exponent */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_EXPONENT]) {
hddLog(LOGE, FL("attr exponent failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].exponent = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_EXPONENT]);
hddLog(LOG1, FL("exponent %u"),
pReqMsg->buckets[bktIndex].exponent);
/* Parse and fetch step count */
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]) {
hddLog(LOGE, FL("attr step count failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].step_count = nla_get_u32(
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_STEP_COUNT]);
hddLog(LOG1, FL("Step count %u"),
pReqMsg->buckets[bktIndex].step_count);
ccmCfgGetInt(pHddCtx->hHal, WNI_CFG_PASSIVE_MAXIMUM_CHANNEL_TIME, &passive_max_chn_time);
ccmCfgGetInt(pHddCtx->hHal, WNI_CFG_PASSIVE_MAXIMUM_CHANNEL_TIME, &active_max_chn_time);
/* Framework shall pass the channel list if the input WiFi band is
* WIFI_BAND_UNSPECIFIED.
* If the input WiFi band is specified (any value other than
* WIFI_BAND_UNSPECIFIED) then driver populates the channel list
*/
if (pReqMsg->buckets[bktIndex].band != WIFI_BAND_UNSPECIFIED) {
numChannels = 0;
hddLog(LOG1, "WiFi band is specified, driver to fill channel list");
status = sme_GetValidChannelsByBand(pHddCtx->hHal,
pReqMsg->buckets[bktIndex].band,
chanList, &numChannels);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_GetValidChannelsByBand failed (err=%d)"),
status);
return -EINVAL;
}
pReqMsg->buckets[bktIndex].numChannels =
VOS_MIN(numChannels, WLAN_EXTSCAN_MAX_CHANNELS);
hddLog(LOG1, FL("Num channels %d"),
pReqMsg->buckets[bktIndex].numChannels);
for (j = 0; j < pReqMsg->buckets[bktIndex].numChannels;
j++) {
pReqMsg->buckets[bktIndex].channels[j].channel =
chanList[j];
pReqMsg->buckets[bktIndex].channels[j].
chnlClass = 0;
if (CSR_IS_CHANNEL_DFS(
vos_freq_to_chan(chanList[j]))) {
pReqMsg->buckets[bktIndex].channels[j].
passive = 1;
pReqMsg->buckets[bktIndex].channels[j].
dwellTimeMs = passive_max_chn_time;
} else {
pReqMsg->buckets[bktIndex].channels[j].
passive = 0;
pReqMsg->buckets[bktIndex].channels[j].
dwellTimeMs = active_max_chn_time;
}
hddLog(LOG1,
"Channel %u Passive %u Dwell time %u ms",
pReqMsg->buckets[bktIndex].channels[j].channel,
pReqMsg->buckets[bktIndex].channels[j].passive,
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs);
}
bktIndex++;
continue;
}
/* Parse and fetch number of channels */
if (!bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]) {
hddLog(LOGE, FL("attr num channels failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].numChannels =
nla_get_u32(bucket[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS]);
hddLog(LOG1, FL("num channels %d"),
pReqMsg->buckets[bktIndex].numChannels);
if (!bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC]) {
hddLog(LOGE, FL("attr channel spec failed"));
return -EINVAL;
}
j = 0;
nla_for_each_nested(channels,
bucket[QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC], rem2) {
if (nla_parse(channel,
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
nla_data(channels), nla_len(channels),
wlan_hdd_extscan_config_policy)) {
hddLog(LOGE, FL("nla_parse failed"));
return -EINVAL;
}
/* Parse and fetch channel */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]) {
hddLog(LOGE, FL("attr channel failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].channel =
nla_get_u32(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_CHANNEL]);
hddLog(LOG1, FL("channel %u"),
pReqMsg->buckets[bktIndex].channels[j].channel);
/* Parse and fetch dwell time */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]) {
hddLog(LOGE, FL("attr dwelltime failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs =
nla_get_u32(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_DWELL_TIME]);
hddLog(LOG1, FL("Dwell time (%u ms)"),
pReqMsg->buckets[bktIndex].channels[j].dwellTimeMs);
/* Parse and fetch channel spec passive */
if (!channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]) {
hddLog(LOGE,
FL("attr channel spec passive failed"));
return -EINVAL;
}
pReqMsg->buckets[bktIndex].channels[j].passive =
nla_get_u8(channel[
QCA_WLAN_VENDOR_ATTR_EXTSCAN_CHANNEL_SPEC_PASSIVE]);
hddLog(LOG1, FL("Chnl spec passive %u"),
pReqMsg->buckets[bktIndex].channels[j].passive);
j++;
}
if (j != pReqMsg->buckets[bktIndex].numChannels) {
hddLog(LOG1, FL("Input parameters didn't match"));
return -EINVAL;
}
bktIndex++;
}
return 0;
}
/*
* define short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_start()
*/
#define PARAM_MAX \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID
#define PARAM_BASE_PERIOD \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_BASE_PERIOD
#define PARAM_MAX_AP_PER_SCAN \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN
#define PARAM_RPT_THRHLD_PERCENT \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
#define PARAM_RPT_THRHLD_NUM_SCANS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
#define PARAM_NUM_BUCKETS \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
static int __wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tpSirEXTScanStartReqParams pReqMsg = NULL;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
int retval;
eHalStatus status;
tANI_U32 request_id;
struct hdd_ext_scan_context *context;
unsigned long rc;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
pReqMsg = (tpSirEXTScanStartReqParams)
vos_mem_malloc(sizeof(*pReqMsg));
if (!pReqMsg) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
pReqMsg->requestId = nla_get_u32(
tb[PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), pReqMsg->requestId);
pReqMsg->sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), pReqMsg->sessionId);
/* Parse and fetch base period */
if (!tb[PARAM_BASE_PERIOD]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr base period failed"));
goto fail;
}
pReqMsg->basePeriod = nla_get_u32(
tb[PARAM_BASE_PERIOD]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Base Period (%d)"),
pReqMsg->basePeriod);
/* Parse and fetch max AP per scan */
if (!tb[PARAM_MAX_AP_PER_SCAN]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr max_ap_per_scan failed"));
goto fail;
}
pReqMsg->maxAPperScan = nla_get_u32(
tb[PARAM_MAX_AP_PER_SCAN]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Max AP per Scan (%d)"),
pReqMsg->maxAPperScan);
/* Parse and fetch report threshold */
if (!tb[PARAM_RPT_THRHLD_PERCENT]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr report_threshold failed"));
goto fail;
}
pReqMsg->reportThresholdPercent = nla_get_u8(
tb[PARAM_RPT_THRHLD_PERCENT]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Report Threshold (%d)"),
pReqMsg->reportThresholdPercent);
/* Parse and fetch report threshold num scans */
if (!tb[PARAM_RPT_THRHLD_NUM_SCANS]) {
hddLog(LOGE, FL("attr report_threshold num scans failed"));
goto fail;
}
pReqMsg->reportThresholdNumScans = nla_get_u8(
tb[PARAM_RPT_THRHLD_NUM_SCANS]);
hddLog(LOG1, FL("Report Threshold num scans %d"),
pReqMsg->reportThresholdNumScans);
/* Parse and fetch number of buckets */
if (!tb[PARAM_NUM_BUCKETS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr number of buckets failed"));
goto fail;
}
pReqMsg->numBuckets = nla_get_u8(
tb[PARAM_NUM_BUCKETS]);
if (pReqMsg->numBuckets > WLAN_EXTSCAN_MAX_BUCKETS) {
hddLog(VOS_TRACE_LEVEL_WARN, FL("Exceeded MAX number of buckets "
"Setting numBuckets to %u"), WLAN_EXTSCAN_MAX_BUCKETS);
pReqMsg->numBuckets = WLAN_EXTSCAN_MAX_BUCKETS;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Number of Buckets (%d)"),
pReqMsg->numBuckets);
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_BUCKET_SPEC]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bucket spec failed"));
goto fail;
}
pReqMsg->homeAwayTime = pHddCtx->cfg_ini->nRestTimeConc;
if (hdd_extscan_start_fill_bucket_channel_spec(pHddCtx, pReqMsg, tb))
goto fail;
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = pReqMsg->requestId;
spin_unlock(&hdd_context_lock);
status = sme_EXTScanStart(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_EXTScanStart failed(err=%d)"), status);
goto fail;
}
pHddCtx->extscan_start_time_since_boot = vos_get_monotonic_boottime();
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ExtScanStart timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
vos_mem_free(pReqMsg);
EXIT();
return retval;
fail:
vos_mem_free(pReqMsg);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by wlan_hdd_cfg80211_extscan_start()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_BASE_PERIOD
#undef PARAMS_MAX_AP_PER_SCAN
#undef PARAMS_RPT_THRHLD_PERCENT
#undef PARAMS_RPT_THRHLD_NUM_SCANS
#undef PARAMS_NUM_BUCKETS
static int wlan_hdd_cfg80211_extscan_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_start(wiphy, wdev, data, dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tSirEXTScanStopReqParams reqMsg;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
eHalStatus status;
int retval;
unsigned long rc;
struct hdd_ext_scan_context *context;
tANI_U32 request_id;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
reqMsg.requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), reqMsg.requestId);
reqMsg.sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), reqMsg.sessionId);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = reqMsg.requestId;
spin_unlock(&hdd_context_lock);
status = sme_EXTScanStop(pHddCtx->hHal, &reqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_EXTScanStop failed(err=%d)"), status);
return -EINVAL;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ExtScanStop timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
return retval;
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_extscan_stop(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_stop(wiphy, wdev, data, dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
tSirEXTScanResetBssidHotlistReqParams reqMsg;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX + 1];
eHalStatus status;
struct hdd_ext_scan_context *context;
tANI_U32 request_id;
unsigned long rc;
int retval;
ENTER();
if (!wlan_hdd_is_extscan_supported(pAdapter, pHddCtx))
return -EINVAL;
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_MAX,
data, dataLen,
wlan_hdd_extscan_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch request Id */
if (!tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr request id failed"));
return -EINVAL;
}
reqMsg.requestId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_EXTSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Req Id (%d)"), reqMsg.requestId);
reqMsg.sessionId = pAdapter->sessionId;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Session Id (%d)"), reqMsg.sessionId);
context = &pHddCtx->ext_scan_context;
spin_lock(&hdd_context_lock);
INIT_COMPLETION(context->response_event);
context->request_id = request_id = reqMsg.requestId;
spin_unlock(&hdd_context_lock);
status = sme_ResetBssHotlist(pHddCtx->hHal, &reqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ResetBssHotlist failed(err=%d)"), status);
return -EINVAL;
}
/* request was sent -- wait for the response */
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_EXTSCAN));
if (!rc) {
hddLog(LOGE, FL("sme_ResetBssHotlist timed out"));
retval = -ETIMEDOUT;
} else {
spin_lock(&hdd_context_lock);
if (context->request_id == request_id)
retval = context->response_status;
else
retval = -EINVAL;
spin_unlock(&hdd_context_lock);
}
EXIT();
return retval;
}
static int wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int dataLen)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_extscan_reset_bssid_hotlist(wiphy, wdev, data, dataLen);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /* WLAN_FEATURE_EXTSCAN */
/*EXT TDLS*/
static const struct nla_policy
wlan_hdd_tdls_config_enable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS] =
{.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS] = {.type = NLA_S32 },
};
static const struct nla_policy
wlan_hdd_tdls_config_disable_policy[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
};
static const struct nla_policy
wlan_hdd_tdls_config_state_change_policy[
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS] =
{.type = NLA_S32 },
};
static const struct nla_policy
wlan_hdd_tdls_config_get_status_policy[
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX +1] =
{
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR] = {
.type = NLA_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL] = {.type = NLA_S32 },
[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS]
= {.type = NLA_S32 },
};
static const struct nla_policy
wlan_hdd_mac_config[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX+1] =
{
[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI] = {
.type = NLA_UNSPEC,
.len = VOS_MAC_ADDR_FIRST_3_BYTES},
};
static int __wlan_hdd_cfg80211_set_spoofed_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(wdev->netdev);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1];
ENTER();
if (0 != wlan_hdd_validate_context(pHddCtx)){
return -EINVAL;
}
if (!adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
if (adapter->device_mode != WLAN_HDD_INFRA_STATION) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("MAC_SPOOFED_SCAN allowed only in STA"));
return -ENOTSUPP;
}
if (0 == pHddCtx->cfg_ini->enableMacSpoofing) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("MAC_SPOOFED_SCAN disabled in ini"));
return -ENOTSUPP;
}
if (TRUE != sme_IsFeatureSupportedByFW(MAC_SPOOFED_SCAN)){
hddLog(VOS_TRACE_LEVEL_INFO, FL("MAC_SPOOFED_SCAN not supported by FW"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX,
data, data_len, wlan_hdd_mac_config)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(pHddCtx->spoofMacAddr.randomMacAddr.bytes, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]),
VOS_MAC_ADDR_LAST_3_BYTES);
pHddCtx->spoofMacAddr.isEnabled = TRUE;
vos_trace_hex_dump( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]),
VOS_MAC_ADDR_FIRST_3_BYTES);
if ((pHddCtx->spoofMacAddr.randomMacAddr.bytes[0] == 0) &&
(pHddCtx->spoofMacAddr.randomMacAddr.bytes[1] == 0) &&
(pHddCtx->spoofMacAddr.randomMacAddr.bytes[2] == 0))
{
hddLog(LOG1, FL("ZERO MAC OUI Recieved. Disabling Spoofing"));
vos_mem_zero(pHddCtx->spoofMacAddr.randomMacAddr.bytes,
VOS_MAC_ADDRESS_LEN);
pHddCtx->spoofMacAddr.isEnabled = FALSE;
}
schedule_delayed_work(&pHddCtx->spoof_mac_addr_work,
msecs_to_jiffies(MAC_ADDR_SPOOFING_DEFER_INTERVAL));
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_set_spoofed_mac_oui(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_spoofed_mac_oui(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u8 peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX + 1];
eHalStatus ret;
tANI_S32 state;
tANI_S32 reason;
tANI_S32 global_operating_class = 0;
tANI_S32 channel = 0;
struct sk_buff *skb = NULL;
int retVal;
ENTER();
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid HDD context"));
return -EINVAL;
}
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS external control is disabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAX,
data, data_len,
wlan_hdd_tdls_config_get_status_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
wlan_hdd_tdls_get_status(pAdapter, peer, &state, &reason);
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
4 * sizeof(s32) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reason (%d) Status (%d) class (%d) channel (%d) peer" MAC_ADDRESS_STR),
reason,
state,
global_operating_class,
channel,
MAC_ADDR_ARRAY(peer));
if (nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_STATE,
state) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_REASON,
reason) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_GLOBAL_OPERATING_CLASS,
global_operating_class) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_STATUS_CHANNEL,
channel)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
retVal = cfg80211_vendor_cmd_reply(skb);
EXIT();
return retVal;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
static int wlan_hdd_cfg80211_exttdls_get_status(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_get_status(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int wlan_hdd_cfg80211_exttdls_callback(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const tANI_U8* mac,
#else
tANI_U8* mac,
#endif
tANI_S32 state,
tANI_S32 reason,
void *ctx)
{
hdd_adapter_t* pAdapter = (hdd_adapter_t*)ctx;
struct sk_buff *skb = NULL;
tANI_S32 global_operating_class = 0;
tANI_S32 channel = 0;
hdd_context_t *pHddCtx;
ENTER();
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (wlan_hdd_validate_context(pHddCtx)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid HDD context"));
return -EINVAL;
}
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS external control is disabled"));
return -ENOTSUPP;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTTDLS_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE_CHANGE_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Entering "));
hddLog(VOS_TRACE_LEVEL_INFO, "Reason: (%d) Status: (%d) Class: (%d) Channel: (%d)",
reason,
state,
global_operating_class,
channel);
hddLog(VOS_TRACE_LEVEL_WARN, "tdls peer " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(mac));
if (nla_put(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_MAC_ADDR,
VOS_MAC_ADDR_SIZE, mac) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_NEW_STATE,
state) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_REASON,
reason) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_CHANNEL,
channel) ||
nla_put_s32(skb,
QCA_WLAN_VENDOR_ATTR_TDLS_STATE_GLOBAL_OPERATING_CLASS,
global_operating_class)
) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla put fail"));
goto nla_put_failure;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
EXIT();
return (0);
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
static int __wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u8 peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX + 1];
eHalStatus status;
tdls_req_params_t pReqMsg = {0};
int ret;
hdd_adapter_t *pAdapter;
ENTER();
if (!dev) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Dev pointer is NULL"));
return -EINVAL;
}
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid HDD context"));
return -EINVAL;
}
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS external control is disabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX,
data, data_len,
wlan_hdd_tdls_config_enable_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
/* Parse and fetch channel */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr channel failed"));
return -EINVAL;
}
pReqMsg.channel = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_CHANNEL]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Channel Num (%d)"), pReqMsg.channel);
/* Parse and fetch global operating class */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr operating class failed"));
return -EINVAL;
}
pReqMsg.global_operating_class = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_GLOBAL_OPERATING_CLASS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Operating class (%d)"),
pReqMsg.global_operating_class);
/* Parse and fetch latency ms */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr latency failed"));
return -EINVAL;
}
pReqMsg.max_latency_ms = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MAX_LATENCY_MS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Latency (%d)"),
pReqMsg.max_latency_ms);
/* Parse and fetch required bandwidth kbps */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bandwidth failed"));
return -EINVAL;
}
pReqMsg.min_bandwidth_kbps = nla_get_s32(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_ENABLE_MIN_BANDWIDTH_KBPS]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("Bandwidth (%d)"),
pReqMsg.min_bandwidth_kbps);
ret = wlan_hdd_tdls_extctrl_config_peer(pAdapter,
peer,
&pReqMsg,
wlan_hdd_cfg80211_exttdls_callback);
EXIT();
return ret;
}
static int wlan_hdd_cfg80211_exttdls_enable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_enable(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
u8 peer[6] = {0};
struct net_device *dev = wdev->netdev;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX + 1];
eHalStatus status;
int ret;
hdd_adapter_t *pAdapter;
ENTER();
if (!dev) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Dev pointer is NULL"));
return -EINVAL;
}
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adapter is NULL"));
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid HDD context"));
return -EINVAL;
}
if (pHddCtx->cfg_ini->fTDLSExternalControl == FALSE) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS external control is disabled"));
return -ENOTSUPP;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAX,
data, data_len,
wlan_hdd_tdls_config_disable_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch mac address */
if (!tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr mac addr failed"));
return -EINVAL;
}
memcpy(peer, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_TDLS_DISABLE_MAC_ADDR]),
sizeof(peer));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(peer));
ret = wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
EXIT();
return ret;
}
static int wlan_hdd_cfg80211_exttdls_disable(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_exttdls_disable(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int
__wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct sk_buff *skb = NULL;
tANI_U32 fset = 0;
int ret = 0;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
if (wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
hddLog(LOG1, FL("Infra Station mode is supported by driver"));
fset |= WIFI_FEATURE_INFRA;
}
if (TRUE == hdd_is_5g_supported(pHddCtx)) {
hddLog(LOG1, FL("INFRA_5G is supported by firmware"));
fset |= WIFI_FEATURE_INFRA_5G;
}
#ifdef WLAN_FEATURE_P2P
if ((wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) &&
(wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_GO))) {
hddLog(LOG1, FL("WiFi-Direct is supported by driver"));
fset |= WIFI_FEATURE_P2P;
}
#endif
/* Soft-AP is supported currently by default */
fset |= WIFI_FEATURE_SOFT_AP;
/* HOTSPOT is a supplicant feature, enable it by default */
fset |= WIFI_FEATURE_HOTSPOT;
#ifdef WLAN_FEATURE_EXTSCAN
if ((TRUE == pHddCtx->cfg_ini->fEnableEXTScan) &&
sme_IsFeatureSupportedByFW(EXTENDED_SCAN) &&
sme_IsFeatureSupportedByFW(EXT_SCAN_ENHANCED)) {
hddLog(LOG1, FL("Enhanced EXTScan is supported by firmware"));
fset |= WIFI_FEATURE_EXTSCAN;
}
#endif
if (sme_IsFeatureSupportedByFW(NAN)) {
hddLog(LOG1, FL("NAN is supported by firmware"));
fset |= WIFI_FEATURE_NAN;
}
/* D2D RTT is not supported currently by default */
if (sme_IsFeatureSupportedByFW(RTT) &&
pHddCtx->cfg_ini->enable_rtt_support) {
hddLog(LOG1, FL("RTT is supported by firmware and framework"));
fset |= WIFI_FEATURE_D2AP_RTT;
}
if (sme_IsFeatureSupportedByFW(RTT3)) {
hddLog(LOG1, FL("RTT3 is supported by firmware"));
fset |= WIFI_FEATURE_RTT3;
}
#ifdef FEATURE_WLAN_BATCH_SCAN
if (fset & WIFI_FEATURE_EXTSCAN) {
hddLog(LOG1, FL("Batch scan is supported as extscan is supported"));
fset &= ~WIFI_FEATURE_BATCH_SCAN;
} else if (sme_IsFeatureSupportedByFW(BATCH_SCAN)) {
hddLog(LOG1, FL("Batch scan is supported by firmware"));
fset |= WIFI_FEATURE_BATCH_SCAN;
}
#endif
#ifdef FEATURE_WLAN_SCAN_PNO
if (pHddCtx->cfg_ini->configPNOScanSupport &&
(eHAL_STATUS_SUCCESS == wlan_hdd_is_pno_allowed(pAdapter))) {
hddLog(LOG1, FL("PNO is supported by firmware"));
fset |= WIFI_FEATURE_PNO;
}
#endif
/* STA+STA is supported currently by default */
fset |= WIFI_FEATURE_ADDITIONAL_STA;
#ifdef FEATURE_WLAN_TDLS
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSSupport) &&
sme_IsFeatureSupportedByFW(TDLS)) {
hddLog(LOG1, FL("TDLS is supported by firmware"));
fset |= WIFI_FEATURE_TDLS;
}
/* TDLS_OFFCHANNEL is not supported currently by default */
#endif
#ifdef WLAN_AP_STA_CONCURRENCY
/* AP+STA concurrency is supported currently by default */
fset |= WIFI_FEATURE_AP_STA;
#endif
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
if ((TRUE == pHddCtx->cfg_ini->fEnableLLStats) &&
(TRUE == sme_IsFeatureSupportedByFW(LINK_LAYER_STATS_MEAS))) {
fset |= WIFI_FEATURE_LINK_LAYER_STATS;
hddLog(LOG1, FL("Link layer stats is supported by driver"));
}
#endif
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) +
NLMSG_HDRLEN);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -EINVAL;
}
hddLog(LOG1, FL("Supported Features : 0x%x"), fset);
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_FEATURE_SET, fset)) {
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
ret = cfg80211_vendor_cmd_reply(skb);
EXIT();
return ret;
nla_put_failure:
kfree_skb(skb);
return -EINVAL;
}
static int
wlan_hdd_cfg80211_get_supported_features(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_supported_features(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
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 },
};
static int
__wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
VOS_STATUS status;
uint32_t ring_id;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX + 1];
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret) {
return ret;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_get_ring_data_policy)) {
hddLog(LOGE, FL("Invalid attribute"));
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]) {
hddLog(LOGE, FL("attr ATTR failed"));
return -EINVAL;
}
ring_id = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_GET_RING_DATA_ID]);
hddLog(LOG1, FL("Bug report triggered by framework"));
status = vos_fatal_event_logs_req(WLAN_LOG_TYPE_NON_FATAL,
WLAN_LOG_INDICATOR_FRAMEWORK,
WLAN_LOG_REASON_CODE_FRAMEWORK,
TRUE, TRUE
);
if (VOS_STATUS_SUCCESS != status) {
hddLog(LOGE, FL("Failed to trigger bug report"));
return -EINVAL;
}
return 0;
}
static int
wlan_hdd_cfg80211_wifi_logger_get_ring_data(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_logger_get_ring_data(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#define MAX_CONCURRENT_MATRIX \
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_MAX
#define MATRIX_CONFIG_PARAM_SET_SIZE_MAX \
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_CONFIG_PARAM_SET_SIZE_MAX
static const struct nla_policy
wlan_hdd_get_concurrency_matrix_policy[MAX_CONCURRENT_MATRIX + 1] = {
[MATRIX_CONFIG_PARAM_SET_SIZE_MAX] = {.type = NLA_U32},
};
static int
__wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
uint32_t feature_set_matrix[WLAN_HDD_MAX_FEATURE_SET] = {0};
uint8_t i, feature_sets, max_feature_sets;
struct nlattr *tb[MAX_CONCURRENT_MATRIX + 1];
struct sk_buff *reply_skb;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
int ret;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
if (nla_parse(tb, MAX_CONCURRENT_MATRIX, data, data_len,
wlan_hdd_get_concurrency_matrix_policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch max feature set */
if (!tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]) {
hddLog(LOGE, FL("Attr max feature set size failed"));
return -EINVAL;
}
max_feature_sets = nla_get_u32(tb[MATRIX_CONFIG_PARAM_SET_SIZE_MAX]);
hddLog(LOG1, FL("Max feature set size (%d)"), max_feature_sets);
/* Fill feature combination matrix */
feature_sets = 0;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
WIFI_FEATURE_P2P;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
WIFI_FEATURE_SOFT_AP;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_P2P |
WIFI_FEATURE_SOFT_AP;
feature_set_matrix[feature_sets++] = WIFI_FEATURE_INFRA |
WIFI_FEATURE_SOFT_AP |
WIFI_FEATURE_P2P;
/* Add more feature combinations here */
feature_sets = VOS_MIN(feature_sets, max_feature_sets);
hddLog(LOG1, FL("Number of feature sets (%d)"), feature_sets);
hddLog(LOG1, "Feature set matrix");
for (i = 0; i < feature_sets; i++)
hddLog(LOG1, "[%d] 0x%02X", i, feature_set_matrix[i]);
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(u32) +
sizeof(u32) * feature_sets +
NLMSG_HDRLEN);
if (reply_skb) {
if (nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET_SIZE,
feature_sets) ||
nla_put(reply_skb,
QCA_WLAN_VENDOR_ATTR_GET_CONCURRENCY_MATRIX_RESULTS_SET,
sizeof(u32) * feature_sets, feature_set_matrix)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
ret = cfg80211_vendor_cmd_reply(reply_skb);
EXIT();
return ret;
}
hddLog(LOGE, FL("Feature set matrix: buffer alloc fail"));
return -ENOMEM;
}
#undef MAX_CONCURRENT_MATRIX
#undef MATRIX_CONFIG_PARAM_SET_SIZE_MAX
static int
wlan_hdd_cfg80211_get_concurrency_matrix(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_concurrency_matrix(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static int
__wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
int ret;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
if( !pHddCtx->cfg_ini->enableFwrMemDump ||
(FALSE == sme_IsFeatureSupportedByFW(MEMORY_DUMP_SUPPORTED)))
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("FW dump Logging not supported"));
return -EOPNOTSUPP;
}
/*call common API for FW mem dump req*/
ret = wlan_hdd_fw_mem_dump_req(pHddCtx);
if (!ret)
{
/*indicate to userspace the status of fw mem dump */
wlan_indicate_mem_dump_complete(true);
}
else
{
/*else send failure to userspace */
wlan_indicate_mem_dump_complete(false);
}
EXIT();
return ret;
}
/**
* wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the NL data.
* @data_len:Length of @data
*
* This is called when wlan driver needs to get the firmware memory dump
* via vendor specific command.
*
* Return: 0 on success, error number otherwise.
*/
static int
wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data,
data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct
nla_policy
qca_wlan_vendor_wifi_logger_start_policy
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]
= {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]
= {.type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]
= {.type = NLA_U32 },
};
/**
* __wlan_hdd_cfg80211_wifi_logger_start() - This function is used to enable
* or disable the collection of packet statistics from the firmware
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function is used to enable or disable the collection of packet
* statistics from the firmware
*
* Return: 0 on success and errno on failure
*/
static int __wlan_hdd_cfg80211_wifi_logger_start(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
eHalStatus status;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX + 1];
tAniWifiStartLog start_log;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status) {
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_MAX,
data, data_len,
qca_wlan_vendor_wifi_logger_start_policy)) {
hddLog(LOGE, FL("Invalid attribute"));
return -EINVAL;
}
/* Parse and fetch ring id */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]) {
hddLog(LOGE, FL("attr ATTR failed"));
return -EINVAL;
}
start_log.ringId = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID]);
hddLog(LOG1, FL("Ring ID=%d"), start_log.ringId);
/* Parse and fetch verbose level */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]) {
hddLog(LOGE, FL("attr verbose_level failed"));
return -EINVAL;
}
start_log.verboseLevel = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL]);
hddLog(LOG1, FL("verbose_level=%d"), start_log.verboseLevel);
/* Parse and fetch flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]) {
hddLog(LOGE, FL("attr flag failed"));
return -EINVAL;
}
start_log.flag = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS]);
hddLog(LOG1, FL("flag=%d"), start_log.flag);
if ((RING_ID_PER_PACKET_STATS == start_log.ringId) &&
(!hdd_ctx->cfg_ini->wlanPerPktStatsLogEnable ||
!vos_isPktStatsEnabled()))
{
hddLog(LOGE, FL("per pkt stats not enabled"));
return -EINVAL;
}
vos_set_ring_log_level(start_log.ringId, start_log.verboseLevel);
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)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_logger_start(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
static const struct nla_policy
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 },
};
static int __wlan_hdd_cfg80211_disable_dfs_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX + 1];
eHalStatus status;
u32 dfsFlag = 0;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG_MAX,
data, data_len,
wlan_hdd_set_no_dfs_flag_config_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch required bandwidth kbps */
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr dfs flag failed"));
return -EINVAL;
}
dfsFlag = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_SET_NO_DFS_FLAG]);
hddLog(VOS_TRACE_LEVEL_INFO, FL(" DFS flag (%d)"),
dfsFlag);
pHddCtx->disable_dfs_flag = dfsFlag;
sme_disable_dfs_channel(hHal, dfsFlag);
sme_FilterScanResults(hHal, pAdapter->sessionId);
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_disable_dfs_channels(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_disable_dfs_channels(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
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_UNSPEC,
.len = HDD_MAC_ADDR_LEN},
};
static int __wlan_hdd_cfg80211_firmware_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
u8 bssid[6] = {0};
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
eHalStatus status = eHAL_STATUS_SUCCESS;
v_U32_t isFwrRoamEnabled = FALSE;
int ret;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret) {
return ret;
}
ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX,
data, data_len,
qca_wlan_vendor_attr);
if (ret){
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid ATTR"));
return -EINVAL;
}
/* Parse and fetch Enable flag */
if (!tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr enable failed"));
return -EINVAL;
}
isFwrRoamEnabled = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]);
hddLog(VOS_TRACE_LEVEL_INFO, FL("isFwrRoamEnabled (%d)"), isFwrRoamEnabled);
/* Parse and fetch bssid */
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("attr bss id failed"));
return -EINVAL;
}
memcpy(bssid, nla_data(
tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
sizeof(bssid));
hddLog(VOS_TRACE_LEVEL_INFO, FL(MAC_ADDRESS_STR),MAC_ADDR_ARRAY(bssid));
//Update roaming
status = sme_ConfigFwrRoaming((tHalHandle)(pHddCtx->hHal), isFwrRoamEnabled);
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_ConfigFwrRoaming failed (err=%d)"), status);
return -EINVAL;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_firmware_roaming(struct wiphy *wiphy,
struct wireless_dev *wdev, const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_firmware_roaming(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
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 },
};
/**
* __wlan_hdd_cfg80211_get_wifi_info() - Get the wifi driver related info
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called when wlan driver needs to send wifi driver related info
* (driver/fw version) to the user space application upon request.
*
* Return: Return the Success or Failure code.
*/
static int __wlan_hdd_cfg80211_get_wifi_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX + 1];
tSirVersionString version;
uint32 version_len;
uint8 attr;
int status;
struct sk_buff *reply_skb = NULL;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status) {
hddLog(LOGE, FL("HDD context is not valid"));
return -EINVAL;
}
if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX, data,
data_len, qca_wlan_vendor_get_wifi_info_policy)) {
hddLog(LOGE, FL("WIFI_INFO_GET NL CMD parsing failed"));
return -EINVAL;
}
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION]) {
hddLog(LOG1, FL("Rcvd req for Driver version Driver version is %s"),
QWLAN_VERSIONSTR);
strlcpy(version, QWLAN_VERSIONSTR, sizeof(version));
attr = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION;
} else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION]) {
hddLog(LOG1, FL("Rcvd req for FW version FW version is %s"),
hdd_ctx->fw_Version);
strlcpy(version, hdd_ctx->fw_Version, sizeof(version));
attr = QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION;
} else {
hddLog(LOGE, FL("Invalid attribute in get wifi info request"));
return -EINVAL;
}
version_len = strlen(version);
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
version_len + NLA_HDRLEN + NLMSG_HDRLEN);
if (!reply_skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
if (nla_put(reply_skb, attr, version_len, version)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* __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
* @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_get_wifi_info(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_wifi_info(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_monitor_rssi()
*/
#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX
#define PARAM_REQUEST_ID QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID
#define PARAM_CONTROL QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL
#define PARAM_MIN_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI
#define PARAM_MAX_RSSI QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI
/**---------------------------------------------------------------------------
\brief hdd_rssi_monitor_start_done - callback to be executed when rssi
monitor start is completed successfully.
\return - None
--------------------------------------------------------------------------*/
void hdd_rssi_monitor_start_done(void *fwRssiMonitorCbContext, VOS_STATUS status)
{
hdd_context_t* pHddCtx = (hdd_context_t*)fwRssiMonitorCbContext;
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is NULL",__func__);
return;
}
if (VOS_STATUS_SUCCESS == status)
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Rssi Monitor start successful"));
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Rssi Monitor start not successful"));
}
return;
}
/**---------------------------------------------------------------------------
\brief hdd_rssi_monitor_stop_done - callback to be executed when rssi monitor
stop is completed successfully.
\return - None
--------------------------------------------------------------------------*/
void hdd_rssi_monitor_stop_done(void *fwRssiMonitorCbContext, VOS_STATUS status)
{
hdd_context_t* pHddCtx = (hdd_context_t*)fwRssiMonitorCbContext;
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is NULL",__func__);
return;
}
if (VOS_STATUS_SUCCESS == status)
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Rssi Monitor stop successful"));
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Rssi Monitor stop not successful"));
}
return;
}
/**
* __wlan_hdd_cfg80211_monitor_rssi() - monitor rssi
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
hdd_station_ctx_t *pHddStaCtx;
struct nlattr *tb[PARAM_MAX + 1];
tpSirRssiMonitorReq pReq;
eHalStatus status;
int ret;
uint32_t control;
static const struct nla_policy policy[PARAM_MAX + 1] = {
[PARAM_REQUEST_ID] = { .type = NLA_U32 },
[PARAM_CONTROL] = { .type = NLA_U32 },
[PARAM_MIN_RSSI] = { .type = NLA_S8 },
[PARAM_MAX_RSSI] = { .type = NLA_S8 },
};
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret) {
return -EINVAL;
}
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len, policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[PARAM_REQUEST_ID]) {
hddLog(LOGE, FL("attr request id failed"));
return -EINVAL;
}
if (!tb[PARAM_CONTROL]) {
hddLog(LOGE, FL("attr control failed"));
return -EINVAL;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
pReq = vos_mem_malloc(sizeof(tSirRssiMonitorReq));
if(NULL == pReq)
{
hddLog(LOGE,
FL("vos_mem_alloc failed "));
return eHAL_STATUS_FAILED_ALLOC;
}
vos_mem_set(pReq, sizeof(tSirRssiMonitorReq), 0);
pReq->requestId = nla_get_u32(tb[PARAM_REQUEST_ID]);
pReq->sessionId = pAdapter->sessionId;
pReq->rssiMonitorCbContext = hdd_ctx;
control = nla_get_u32(tb[PARAM_CONTROL]);
vos_mem_copy( &pReq->currentBssId, pHddStaCtx->conn_info.bssId, WNI_CFG_BSSID_LEN);
hddLog(LOG1, FL("Request Id: %u Session_id: %d Control: %d"),
pReq->requestId, pReq->sessionId, control);
if (control == QCA_WLAN_RSSI_MONITORING_START) {
if (!tb[PARAM_MIN_RSSI]) {
hddLog(LOGE, FL("attr min rssi failed"));
goto fail;
}
if (!tb[PARAM_MAX_RSSI]) {
hddLog(LOGE, FL("attr max rssi failed"));
goto fail;
}
pReq->minRssi = nla_get_s8(tb[PARAM_MIN_RSSI]);
pReq->maxRssi = nla_get_s8(tb[PARAM_MAX_RSSI]);
pReq->rssiMonitorCallback = hdd_rssi_monitor_start_done;
if (!(pReq->minRssi < pReq->maxRssi)) {
hddLog(LOGW, FL("min_rssi: %d must be less than max_rssi: %d"),
pReq->minRssi, pReq->maxRssi);
goto fail;
}
hddLog(LOG1, FL("Min_rssi: %d Max_rssi: %d"),
pReq->minRssi, pReq->maxRssi);
status = sme_StartRssiMonitoring(hdd_ctx->hHal, pReq);
}
else if (control == QCA_WLAN_RSSI_MONITORING_STOP) {
pReq->rssiMonitorCallback = hdd_rssi_monitor_stop_done;
status = sme_StopRssiMonitoring(hdd_ctx->hHal, pReq);
}
else {
hddLog(LOGE, FL("Invalid control cmd: %d"), control);
goto fail;
}
if (!HAL_STATUS_SUCCESS(status)) {
hddLog(LOGE,
FL("sme_set_rssi_monitoring failed(err=%d)"), status);
goto fail;
}
return 0;
fail:
vos_mem_free(pReq);
return -EINVAL;
}
/*
* done with short names for the global vendor params
* used by __wlan_hdd_cfg80211_monitor_rssi()
*/
#undef PARAM_MAX
#undef PARAM_CONTROL
#undef PARAM_REQUEST_ID
#undef PARAM_MAX_RSSI
#undef PARAM_MIN_RSSI
/**
* wlan_hdd_cfg80211_monitor_rssi() - SSR wrapper to rssi monitoring
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int
wlan_hdd_cfg80211_monitor_rssi(struct wiphy *wiphy, struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_monitor_rssi(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_rssi_threshold_breached_cb() - rssi breached NL event
* @hddctx: HDD context
* @data: rssi breached event data
*
* This function reads the rssi breached event %data and fill in the skb with
* NL attributes and send up the NL event.
* This callback execute in atomic context and must not invoke any
* blocking calls.
*
* Return: none
*/
void hdd_rssi_threshold_breached_cb(void *hddctx,
struct rssi_breach_event *data)
{
hdd_context_t *pHddCtx = (hdd_context_t *)hddctx;
int status;
struct sk_buff *skb;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status) {
return;
}
if (!data) {
hddLog(LOGE, FL("data is null"));
return;
}
skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
NULL,
#endif
EXTSCAN_EVENT_BUF_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX,
GFP_KERNEL);
if (!skb) {
hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
return;
}
hddLog(LOG1, "Req Id: %u Current rssi: %d",
data->request_id, data->curr_rssi);
hddLog(LOG1, "Current BSSID: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(data->curr_bssid.bytes));
if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
data->request_id) ||
nla_put(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
sizeof(data->curr_bssid), data->curr_bssid.bytes) ||
nla_put_s8(skb, QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
data->curr_rssi)) {
hddLog(LOGE, FL("nla put fail"));
goto fail;
}
cfg80211_vendor_event(skb, GFP_KERNEL);
return;
fail:
kfree_skb(skb);
return;
}
/**
* __wlan_hdd_cfg80211_setband() - set band
* @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_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
int ret;
static const struct nla_policy policy[QCA_WLAN_VENDOR_ATTR_MAX + 1]
= {[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE] = { .type = NLA_U32 }};
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret) {
hddLog(LOGE, FL("HDD context is not valid"));
return ret;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]) {
hddLog(LOGE, FL("attr QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE failed"));
return -EINVAL;
}
hdd_ctx->isSetBandByNL = TRUE;
ret = hdd_setBand(dev,
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE]));
hdd_ctx->isSetBandByNL = FALSE;
EXIT();
return ret;
}
/**
* wlan_hdd_cfg80211_setband() - 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_setband(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_setband(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
/**
* hdd_map_req_id_to_pattern_id() - map request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* if the slot is available, store the request id and return pattern id
* if entry exists, return the pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_map_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
{
if (hdd_ctx->op_ctx.op_table[i].request_id == 0)
{
hdd_ctx->op_ctx.op_table[i].request_id = request_id;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
} else if (hdd_ctx->op_ctx.op_table[i].request_id ==
request_id) {
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -EINVAL;
}
/**
* hdd_unmap_req_id_to_pattern_id() - unmap request id to pattern id
* @hdd_ctx: HDD context
* @request_id: [input] request id
* @pattern_id: [output] pattern id
*
* This function loops through request id to pattern id array
* reset request id to 0 (slot available again) and
* return pattern id
*
* Return: 0 on success and errno on failure
*/
static int hdd_unmap_req_id_to_pattern_id(hdd_context_t *hdd_ctx,
uint32_t request_id,
uint8_t *pattern_id)
{
uint32_t i;
mutex_lock(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
{
if (hdd_ctx->op_ctx.op_table[i].request_id == request_id)
{
hdd_ctx->op_ctx.op_table[i].request_id = 0;
*pattern_id = hdd_ctx->op_ctx.op_table[i].pattern_id;
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return 0;
}
}
mutex_unlock(&hdd_ctx->op_ctx.op_lock);
return -EINVAL;
}
/*
* define short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#define PARAM_MAX QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX
#define PARAM_REQUEST_ID \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID
#define PARAM_CONTROL \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL
#define PARAM_IP_PACKET \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA
#define PARAM_SRC_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR
#define PARAM_DST_MAC_ADDR \
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR
#define PARAM_PERIOD QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD
/**
* wlan_hdd_add_tx_ptrn() - add tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a AddTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_add_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
struct nlattr **tb)
{
struct sSirAddPeriodicTxPtrn *add_req;
eHalStatus status;
uint32_t request_id, ret, len;
uint8_t pattern_id = 0;
v_MACADDR_t dst_addr;
uint16_t eth_type = htons(ETH_P_IP);
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter)))
{
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
add_req = vos_mem_malloc(sizeof(*add_req));
if (!add_req)
{
hddLog(LOGE, FL("memory allocation failed"));
return -ENOMEM;
}
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID])
{
hddLog(LOGE, FL("attr request id failed"));
goto fail;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
hddLog(LOG1, FL("Request Id: %u"), request_id);
if (request_id == 0)
{
hddLog(LOGE, FL("request_id cannot be zero"));
goto fail;
}
if (!tb[PARAM_PERIOD])
{
hddLog(LOGE, FL("attr period failed"));
goto fail;
}
add_req->usPtrnIntervalMs = nla_get_u32(tb[PARAM_PERIOD]);
hddLog(LOG1, FL("Period: %u ms"), add_req->usPtrnIntervalMs);
if (add_req->usPtrnIntervalMs == 0)
{
hddLog(LOGE, FL("Invalid interval zero, return failure"));
goto fail;
}
if (!tb[PARAM_SRC_MAC_ADDR])
{
hddLog(LOGE, FL("attr source mac address failed"));
goto fail;
}
nla_memcpy(add_req->macAddress, tb[PARAM_SRC_MAC_ADDR],
VOS_MAC_ADDR_SIZE);
hddLog(LOG1, "input src mac address: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(add_req->macAddress));
if (memcmp(add_req->macAddress, adapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE))
{
hddLog(LOGE,
FL("input src mac address and connected ap bssid are different"));
goto fail;
}
if (!tb[PARAM_DST_MAC_ADDR])
{
hddLog(LOGE, FL("attr dst mac address failed"));
goto fail;
}
nla_memcpy(dst_addr.bytes, tb[PARAM_DST_MAC_ADDR], VOS_MAC_ADDR_SIZE);
hddLog(LOG1, "input dst mac address: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(dst_addr.bytes));
if (!tb[PARAM_IP_PACKET])
{
hddLog(LOGE, FL("attr ip packet failed"));
goto fail;
}
add_req->ucPtrnSize = nla_len(tb[PARAM_IP_PACKET]);
hddLog(LOG1, FL("IP packet len: %u"), add_req->ucPtrnSize);
if (add_req->ucPtrnSize < 0 ||
add_req->ucPtrnSize > (PERIODIC_TX_PTRN_MAX_SIZE -
HDD_ETH_HEADER_LEN))
{
hddLog(LOGE, FL("Invalid IP packet len: %d"),
add_req->ucPtrnSize);
goto fail;
}
len = 0;
vos_mem_copy(&add_req->ucPattern[0], dst_addr.bytes, VOS_MAC_ADDR_SIZE);
len += VOS_MAC_ADDR_SIZE;
vos_mem_copy(&add_req->ucPattern[len], add_req->macAddress,
VOS_MAC_ADDR_SIZE);
len += VOS_MAC_ADDR_SIZE;
vos_mem_copy(&add_req->ucPattern[len], &eth_type, 2);
len += 2;
/*
* This is the IP packet, add 14 bytes Ethernet (802.3) header
* ------------------------------------------------------------
* | 14 bytes Ethernet (802.3) header | IP header and payload |
* ------------------------------------------------------------
*/
vos_mem_copy(&add_req->ucPattern[len],
nla_data(tb[PARAM_IP_PACKET]),
add_req->ucPtrnSize);
add_req->ucPtrnSize += len;
VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
add_req->ucPattern, add_req->ucPtrnSize);
ret = hdd_map_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret)
{
hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
goto fail;
}
add_req->ucPtrnId = pattern_id;
hddLog(LOG1, FL("pattern id: %d"), add_req->ucPtrnId);
status = sme_AddPeriodicTxPtrn(hdd_ctx->hHal, add_req);
if (!HAL_STATUS_SUCCESS(status))
{
hddLog(LOGE,
FL("sme_AddPeriodicTxPtrn failed (err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(add_req);
return 0;
fail:
vos_mem_free(add_req);
return -EINVAL;
}
/**
* wlan_hdd_del_tx_ptrn() - delete tx pattern
* @adapter: adapter pointer
* @hdd_ctx: hdd context
* @tb: nl attributes
*
* This function reads the NL attributes and forms a DelTxPtrn message
* posts it to SME.
*
*/
static int
wlan_hdd_del_tx_ptrn(hdd_adapter_t *adapter, hdd_context_t *hdd_ctx,
struct nlattr **tb)
{
struct sSirDelPeriodicTxPtrn *del_req;
eHalStatus status;
uint32_t request_id, ret;
uint8_t pattern_id = 0;
/* Parse and fetch request Id */
if (!tb[PARAM_REQUEST_ID])
{
hddLog(LOGE, FL("attr request id failed"));
return -EINVAL;
}
request_id = nla_get_u32(tb[PARAM_REQUEST_ID]);
if (request_id == 0)
{
hddLog(LOGE, FL("request_id cannot be zero"));
return -EINVAL;
}
ret = hdd_unmap_req_id_to_pattern_id(hdd_ctx, request_id, &pattern_id);
if (ret)
{
hddLog(LOGW, FL("req id to pattern id failed (ret=%d)"), ret);
return -EINVAL;
}
del_req = vos_mem_malloc(sizeof(*del_req));
if (!del_req)
{
hddLog(LOGE, FL("memory allocation failed"));
return -ENOMEM;
}
vos_mem_set(del_req, sizeof(*del_req), 0);
vos_mem_copy(del_req->macAddress, adapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE);
hddLog(LOG1, MAC_ADDRESS_STR, MAC_ADDR_ARRAY(del_req->macAddress));
del_req->ucPatternIdBitmap |= (0x1 << pattern_id);
hddLog(LOG1, FL("Request Id: %u Pattern id: %d, bitmap %04x"),
request_id, pattern_id, del_req->ucPatternIdBitmap);
status = sme_DelPeriodicTxPtrn(hdd_ctx->hHal, del_req);
if (!HAL_STATUS_SUCCESS(status))
{
hddLog(LOGE,
FL("sme_DelPeriodicTxPtrn failed (err=%d)"), status);
goto fail;
}
EXIT();
vos_mem_free(del_req);
return 0;
fail:
vos_mem_free(del_req);
return -EINVAL;
}
/**
* __wlan_hdd_cfg80211_offloaded_packets() - send offloaded packets
* @wiphy: Pointer to wireless phy
* @wdev: Pointer to wireless device
* @data: Pointer to data
* @data_len: Data length
*
* Return: 0 on success, negative errno on failure
*/
static int
__wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct nlattr *tb[PARAM_MAX + 1];
uint8_t control;
int ret;
static const struct nla_policy policy[PARAM_MAX + 1] =
{
[PARAM_REQUEST_ID] = { .type = NLA_U32 },
[PARAM_CONTROL] = { .type = NLA_U32 },
[PARAM_SRC_MAC_ADDR] = { .type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE },
[PARAM_DST_MAC_ADDR] = { .type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE },
[PARAM_PERIOD] = { .type = NLA_U32 },
};
ENTER();
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
{
hddLog(LOGE, FL("HDD context is not valid"));
return ret;
}
if (!sme_IsFeatureSupportedByFW(WLAN_PERIODIC_TX_PTRN))
{
hddLog(LOGE,
FL("Periodic Tx Pattern Offload feature is not supported in FW!"));
return -ENOTSUPP;
}
if (nla_parse(tb, PARAM_MAX, data, data_len, policy))
{
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
if (!tb[PARAM_CONTROL])
{
hddLog(LOGE, FL("attr control failed"));
return -EINVAL;
}
control = nla_get_u32(tb[PARAM_CONTROL]);
hddLog(LOG1, FL("Control: %d"), control);
if (control == WLAN_START_OFFLOADED_PACKETS)
return wlan_hdd_add_tx_ptrn(adapter, hdd_ctx, tb);
else if (control == WLAN_STOP_OFFLOADED_PACKETS)
return wlan_hdd_del_tx_ptrn(adapter, hdd_ctx, tb);
else
{
hddLog(LOGE, FL("Invalid control: %d"), control);
return -EINVAL;
}
}
/*
* done with short names for the global vendor params
* used by __wlan_hdd_cfg80211_offloaded_packets()
*/
#undef PARAM_MAX
#undef PARAM_REQUEST_ID
#undef PARAM_CONTROL
#undef PARAM_IP_PACKET
#undef PARAM_SRC_MAC_ADDR
#undef PARAM_DST_MAC_ADDR
#undef PARAM_PERIOD
/**
* wlan_hdd_cfg80211_offloaded_packets() - Wrapper to offload packets
* @wiphy: wiphy structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of @data
*
* Return: 0 on success; errno on failure
*/
static int wlan_hdd_cfg80211_offloaded_packets(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_offloaded_packets(wiphy,
wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
static const struct
nla_policy
qca_wlan_vendor_attr_policy[QCA_WLAN_VENDOR_ATTR_MAX+1] = {
[QCA_WLAN_VENDOR_ATTR_MAC_ADDR] = {
.type = NLA_BINARY,
.len = HDD_MAC_ADDR_LEN},
};
/**
* wlan_hdd_cfg80211_get_link_properties() - This function is used to
* get link properties like nss, rate flags and operating frequency for
* the connection with the given peer.
* @wiphy: WIPHY structure pointer
* @wdev: Wireless device structure pointer
* @data: Pointer to the data received
* @data_len: Length of the data received
*
* This function return the above link properties on success.
*
* Return: 0 on success and errno on failure
*/
static int wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *hdd_sta_ctx;
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX+1];
uint8_t peer_mac[VOS_MAC_ADDR_SIZE];
uint32_t sta_id;
struct sk_buff *reply_skb;
uint32_t rate_flags = 0;
uint8_t nss;
uint8_t final_rate_flags = 0;
uint32_t freq;
v_CONTEXT_t pVosContext = NULL;
ptSapContext pSapCtx = NULL;
if (0 != wlan_hdd_validate_context(hdd_ctx)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is not valid"));
return -EINVAL;
}
if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_MAX, data, data_len,
qca_wlan_vendor_attr_policy)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid attribute"));
return -EINVAL;
}
if (!tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Attribute peerMac not provided for mode=%d"),
adapter->device_mode);
return -EINVAL;
}
if (nla_len(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]) < sizeof(peer_mac)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Attribute peerMac is invalid=%d"),
adapter->device_mode);
return -EINVAL;
}
memcpy(peer_mac, nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]),
sizeof(peer_mac));
hddLog(VOS_TRACE_LEVEL_INFO,
FL("peerMac="MAC_ADDRESS_STR" for device_mode:%d"),
MAC_ADDR_ARRAY(peer_mac), adapter->device_mode);
if (adapter->device_mode == WLAN_HDD_INFRA_STATION ||
adapter->device_mode == WLAN_HDD_P2P_CLIENT) {
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if ((hdd_sta_ctx->conn_info.connState !=
eConnectionState_Associated) ||
!vos_mem_compare(hdd_sta_ctx->conn_info.bssId, peer_mac,
VOS_MAC_ADDRESS_LEN)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not Associated to mac "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = 1; //pronto supports only one spatial stream
freq = vos_chan_to_freq(
hdd_sta_ctx->conn_info.operationChannel);
rate_flags = hdd_sta_ctx->conn_info.rate_flags;
} else if (adapter->device_mode == WLAN_HDD_P2P_GO ||
adapter->device_mode == WLAN_HDD_SOFTAP) {
pVosContext = ( WLAN_HDD_GET_CTX(adapter))->pvosContext;
pSapCtx = VOS_GET_SAP_CB(pVosContext);
if(pSapCtx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("psapCtx is NULL"));
return -ENOENT;
}
for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) {
if (pSapCtx->aStaInfo[sta_id].isUsed &&
!vos_is_macaddr_broadcast(
&pSapCtx->aStaInfo[sta_id].macAddrSTA) &&
vos_mem_compare(
&pSapCtx->aStaInfo[sta_id].macAddrSTA,
peer_mac, VOS_MAC_ADDRESS_LEN))
break;
}
if (WLAN_MAX_STA_COUNT == sta_id) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("No active peer with mac="MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
nss = 1; //pronto supports only one spatial stream
freq = vos_chan_to_freq(
(WLAN_HDD_GET_AP_CTX_PTR(adapter))->operatingChannel);
rate_flags = pSapCtx->aStaInfo[sta_id].rate_flags;
} else {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not Associated! with mac"MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(peer_mac));
return -EINVAL;
}
if (!(rate_flags & eHAL_TX_RATE_LEGACY)) {
if (rate_flags & eHAL_TX_RATE_VHT80) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
final_rate_flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
} else if (rate_flags & eHAL_TX_RATE_VHT40) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
} else if (rate_flags & eHAL_TX_RATE_VHT20) {
final_rate_flags |= RATE_INFO_FLAGS_VHT_MCS;
} else if (rate_flags & (eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40)) {
final_rate_flags |= RATE_INFO_FLAGS_MCS;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
if (rate_flags & eHAL_TX_RATE_HT40)
final_rate_flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
if (rate_flags & eHAL_TX_RATE_SGI) {
if (!(final_rate_flags & RATE_INFO_FLAGS_VHT_MCS))
final_rate_flags |= RATE_INFO_FLAGS_MCS;
final_rate_flags |= RATE_INFO_FLAGS_SHORT_GI;
}
}
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(u8) + sizeof(u8) + sizeof(u32) + NLMSG_HDRLEN);
if (NULL == reply_skb) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("getLinkProperties: skb alloc failed"));
return -EINVAL;
}
if (nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_NSS,
nss) ||
nla_put_u8(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_RATE_FLAGS,
final_rate_flags) ||
nla_put_u32(reply_skb,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_FREQ,
freq)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("nla_put failed"));
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
#define BEACON_MISS_THRESH_2_4 \
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24
#define BEACON_MISS_THRESH_5_0 \
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5
#define PARAM_WIFICONFIG_MAX QCA_WLAN_VENDOR_ATTR_CONFIG_MAX
#define PARAM_MODULATED_DTIM QCA_WLAN_VENDOR_ATTR_CONFIG_MODULATED_DTIM
#define PARAM_STATS_AVG_FACTOR QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR
#define PARAM_GUARD_TIME QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME
#define PARAM_BCNMISS_PENALTY_PARAM_COUNT \
QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS
#define PARAM_FORCE_RSN_IE \
QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE
/*
* hdd_set_qpower() - Process the qpower command and invoke the SME api
* @hdd_ctx: hdd context
* @enable: Value received in the command, 1 for disable and 2 for enable
*
* Return: void
*/
static void hdd_set_qpower(hdd_context_t *hdd_ctx, uint8_t enable)
{
if (!hdd_ctx) {
hddLog(LOGE, "hdd_ctx NULL");
return;
}
sme_set_qpower(hdd_ctx->hHal, enable);
}
/**
* __wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: EOK or other error codes.
*/
static int __wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hdd_station_ctx_t *pHddStaCtx;
struct nlattr *tb[PARAM_WIFICONFIG_MAX + 1];
tpSetWifiConfigParams pReq;
tModifyRoamParamsReqParams modifyRoamParamsReq;
eHalStatus status;
int ret_val;
uint8_t hb_thresh_val;
uint8_t qpower;
static const struct nla_policy policy[PARAM_WIFICONFIG_MAX + 1] = {
[PARAM_STATS_AVG_FACTOR] = { .type = NLA_U16 },
[PARAM_MODULATED_DTIM] = { .type = NLA_U32 },
[PARAM_GUARD_TIME] = { .type = NLA_U32},
[PARAM_BCNMISS_PENALTY_PARAM_COUNT] =
{ .type = NLA_U32},
[BEACON_MISS_THRESH_2_4] = { .type = NLA_U8 },
[BEACON_MISS_THRESH_5_0] = { .type = NLA_U8 },
[PARAM_FORCE_RSN_IE] = {.type = NLA_U8 },
};
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
ret_val = wlan_hdd_validate_context(pHddCtx);
if (ret_val) {
return ret_val;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (nla_parse(tb, PARAM_WIFICONFIG_MAX, data, data_len, policy)) {
hddLog(LOGE, FL("Invalid ATTR"));
return -EINVAL;
}
/* check the Wifi Capability */
if ( (TRUE != pHddCtx->cfg_ini->fEnableWifiConfig) &&
(TRUE != sme_IsFeatureSupportedByFW(WIFI_CONFIG)))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WIFICONFIG not supported by Firmware"));
return -EINVAL;
}
if (tb[PARAM_BCNMISS_PENALTY_PARAM_COUNT]) {
modifyRoamParamsReq.param = WIFI_CONFIG_SET_BCNMISS_PENALTY_COUNT;
modifyRoamParamsReq.value =
nla_get_u32(tb[PARAM_BCNMISS_PENALTY_PARAM_COUNT]);
if (eHAL_STATUS_SUCCESS !=
sme_setBcnMissPenaltyCount(pHddCtx->hHal,&modifyRoamParamsReq))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed", __func__);
ret_val = -EINVAL;
}
return ret_val;
}
/* Moved this down in order to provide provision to set beacon
* miss penalty count irrespective of connection state.
*/
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) {
hddLog(LOGE, FL("Not in Connected state!"));
return -ENOTSUPP;
}
pReq = vos_mem_malloc(sizeof(tSetWifiConfigParams));
if (!pReq) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
"%s: Not able to allocate memory for tSetWifiConfigParams",
__func__);
return eHAL_STATUS_E_MALLOC_FAILED;
}
vos_mem_set(pReq, sizeof(tSetWifiConfigParams), 0);
pReq->sessionId = pAdapter->sessionId;
vos_mem_copy( &pReq->bssId, pHddStaCtx->conn_info.bssId, WNI_CFG_BSSID_LEN);
if (tb[PARAM_MODULATED_DTIM]) {
pReq->paramValue = nla_get_u32(
tb[PARAM_MODULATED_DTIM]);
hddLog(LOG1, FL("Modulated DTIM: pReq->paramValue:%d "),
pReq->paramValue);
pHddCtx->cfg_ini->enableDynamicDTIM = pReq->paramValue;
hdd_set_pwrparams(pHddCtx);
if (BMPS == pmcGetPmcState(pHddCtx->hHal)) {
hddLog( LOG1, FL("WifiConfig: Requesting FullPower!"));
sme_RequestFullPower(WLAN_HDD_GET_HAL_CTX(pAdapter),
iw_full_power_cbfn, pAdapter,
eSME_FULL_PWR_NEEDED_BY_HDD);
}
else
{
hddLog( LOG1, FL("WifiConfig Not in BMPS state"));
}
}
if (tb[PARAM_STATS_AVG_FACTOR]) {
pReq->paramType = WIFI_CONFIG_SET_AVG_STATS_FACTOR;
pReq->paramValue = nla_get_u16(
tb[PARAM_STATS_AVG_FACTOR]);
hddLog(LOG1, FL("AVG_STATS_FACTOR pReq->paramType:%d,pReq->paramValue:%d "),
pReq->paramType, pReq->paramValue);
status = sme_set_wificonfig_params(pHddCtx->hHal, pReq);
if (eHAL_STATUS_SUCCESS != status)
{
vos_mem_free(pReq);
pReq = NULL;
ret_val = -EPERM;
return ret_val;
}
}
if (tb[PARAM_GUARD_TIME]) {
pReq->paramType = WIFI_CONFIG_SET_GUARD_TIME;
pReq->paramValue = nla_get_u32(
tb[PARAM_GUARD_TIME]);
hddLog(LOG1, FL("GUARD_TIME pReq->paramType:%d,pReq->paramValue:%d "),
pReq->paramType, pReq->paramValue);
status = sme_set_wificonfig_params(pHddCtx->hHal, pReq);
if (eHAL_STATUS_SUCCESS != status)
{
vos_mem_free(pReq);
pReq = NULL;
ret_val = -EPERM;
return ret_val;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24]) {
hb_thresh_val = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24]);
hddLog(LOG1, "WLAN set heartbeat threshold for 2.4Ghz %d",
hb_thresh_val);
ccmCfgSetInt((WLAN_HDD_GET_CTX(pAdapter))->hHal,
WNI_CFG_HEART_BEAT_THRESHOLD, hb_thresh_val,
NULL, eANI_BOOLEAN_FALSE);
status = sme_update_hb_threshold(
(WLAN_HDD_GET_CTX(pAdapter))->hHal,
WNI_CFG_HEART_BEAT_THRESHOLD,
hb_thresh_val, eCSR_BAND_24);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(LOGE, "WLAN set heartbeat threshold FAILED %d", status);
vos_mem_free(pReq);
pReq = NULL;
return -EPERM;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5]) {
hb_thresh_val = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5]);
hddLog(LOG1, "WLAN set heartbeat threshold for 5Ghz %d",
hb_thresh_val);
ccmCfgSetInt((WLAN_HDD_GET_CTX(pAdapter))->hHal,
WNI_CFG_HEART_BEAT_THRESHOLD, hb_thresh_val,
NULL, eANI_BOOLEAN_FALSE);
status = sme_update_hb_threshold(
(WLAN_HDD_GET_CTX(pAdapter))->hHal,
WNI_CFG_HEART_BEAT_THRESHOLD,
hb_thresh_val, eCSR_BAND_5G);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(LOGE, "WLAN set heartbeat threshold FAILED %d", status);
vos_mem_free(pReq);
pReq = NULL;
return -EPERM;
}
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]) {
qpower = nla_get_u8(
tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]);
if(qpower > 1) {
hddLog(LOGE, "Invalid QPOWER value %d", qpower);
vos_mem_free(pReq);
pReq = NULL;
return -EINVAL;
}
/* FW is expacting qpower as 1 for Disable and 2 for enable */
qpower++;
hdd_set_qpower(pHddCtx, qpower);
}
if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE] &&
pHddCtx->cfg_ini->force_rsne_override) {
uint8_t force_rsne_override;
force_rsne_override =
nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE]);
if (force_rsne_override > 1) {
hddLog(LOGE, "Invalid test_mode %d", force_rsne_override);
ret_val = -EINVAL;
}
pHddCtx->force_rsne_override = force_rsne_override;
hddLog(LOG1, "force_rsne_override - %d",
pHddCtx->force_rsne_override);
}
EXIT();
return ret_val;
}
/**
* wlan_hdd_cfg80211_wifi_configuration_set() - Wifi configuration
* vendor command
*
* @wiphy: wiphy device pointer
* @wdev: wireless device pointer
* @data: Vendor command data buffer
* @data_len: Buffer length
*
* Handles QCA_WLAN_VENDOR_ATTR_CONFIG_MAX.
*
* Return: EOK or other error codes.
*/
static int wlan_hdd_cfg80211_wifi_configuration_set(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data,
int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_wifi_configuration_set(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* 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_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 },
};
/**
* hdd_set_nud_stats_cb() - hdd callback api to get status
* @data: pointer to adapter
* @rsp: status
*
* Return: None
*/
static void hdd_set_nud_stats_cb(void *data, VOS_STATUS rsp)
{
hdd_adapter_t *adapter = (hdd_adapter_t *)data;
if (NULL == adapter)
return;
if (VOS_STATUS_SUCCESS == rsp) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s success received STATS_SET_START", __func__);
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s STATS_SET_START Failed!!", __func__);
}
return;
}
/**
* __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;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(adapter))->pvosContext;
setArpStatsParams arp_stats_params;
int err = 0;
ENTER();
err = wlan_hdd_validate_context(hdd_ctx);
if (0 != err)
return err;
if (!sme_IsFeatureSupportedByFW(NUD_DEBUG)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s NUD_DEBUG feature not supported by firmware!!", __func__);
return -EINVAL;
}
err = nla_parse(tb, STATS_SET_MAX, data, data_len,
qca_wlan_vendor_set_nud_stats);
if (err)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s STATS_SET_START ATTR", __func__);
return err;
}
if (tb[STATS_SET_START])
{
if (!tb[STATS_GW_IPV4]) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s STATS_SET_START CMD", __func__);
return -EINVAL;
}
arp_stats_params.flag = true;
arp_stats_params.ip_addr = nla_get_u32(tb[STATS_GW_IPV4]);
} else {
arp_stats_params.flag = false;
}
if (arp_stats_params.flag)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s STATS_SET_START Cleared!!", __func__);
vos_mem_zero(&adapter->hdd_stats.hddArpStats,
sizeof(adapter->hdd_stats.hddArpStats));
arp_stats_params.pkt_type = 1; // ARP packet type
if (arp_stats_params.flag) {
hdd_ctx->track_arp_ip = arp_stats_params.ip_addr;
WLANTL_SetARPFWDatapath(pVosContext, true);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s Set FW in data path for ARP with tgt IP :%d",
__func__, hdd_ctx->track_arp_ip);
}
else {
WLANTL_SetARPFWDatapath(pVosContext, false);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s Remove FW from data path", __func__);
}
arp_stats_params.rsp_cb_fn = hdd_set_nud_stats_cb;
arp_stats_params.data_ctx = adapter;
if (eHAL_STATUS_SUCCESS !=
sme_set_nud_debug_stats(hdd_ctx->hHal, &arp_stats_params)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s STATS_SET_START CMD Failed!!", __func__);
return -EINVAL;
}
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 ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_nud_stats(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#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_AP_LINK_DAD
#define STATS_GET_MAX \
QCA_ATTR_NUD_STATS_GET_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 },
};
static void hdd_get_nud_stats_cb(void *data, rsp_stats *rsp)
{
hdd_adapter_t *adapter = (hdd_adapter_t *)data;
hdd_context_t *hdd_ctx;
struct hdd_nud_stats_context *context;
int status;
ENTER();
if (NULL == adapter)
return;
if (!rsp) {
hddLog(LOGE, FL("data is null"));
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status) {
return;
}
adapter->hdd_stats.hddArpStats.tx_fw_cnt = rsp->tx_fw_cnt;
adapter->hdd_stats.hddArpStats.rx_fw_cnt = rsp->rx_fw_cnt;
adapter->hdd_stats.hddArpStats.tx_ack_cnt = rsp->tx_ack_cnt;
adapter->dad |= rsp->dad;
spin_lock(&hdd_context_lock);
context = &hdd_ctx->nud_stats_context;
complete(&context->response_event);
spin_unlock(&hdd_context_lock);
return;
}
static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int err = 0;
unsigned long rc;
struct hdd_nud_stats_context *context;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
getArpStatsParams arp_stats_params;
struct sk_buff *skb;
ENTER();
err = wlan_hdd_validate_context(hdd_ctx);
if (0 != err)
return err;
arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
arp_stats_params.get_rsp_cb_fn = hdd_get_nud_stats_cb;
arp_stats_params.data_ctx = adapter;
spin_lock(&hdd_context_lock);
context = &hdd_ctx->nud_stats_context;
INIT_COMPLETION(context->response_event);
spin_unlock(&hdd_context_lock);
if (!sme_IsFeatureSupportedByFW(NUD_DEBUG)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s NUD_DEBUG feature not supported by firmware!!", __func__);
return -EINVAL;
}
if (eHAL_STATUS_SUCCESS !=
sme_get_nud_debug_stats(hdd_ctx->hHal, &arp_stats_params)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s STATS_SET_START CMD Failed!!", __func__);
return -EINVAL;
}
rc = wait_for_completion_timeout(&context->response_event,
msecs_to_jiffies(WLAN_WAIT_TIME_NUD_STATS));
if (!rc)
{
hddLog(LOGE,
FL("Target response timed out request "));
return -ETIMEDOUT;
}
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
WLAN_NUD_STATS_LEN);
if (!skb)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: cfg80211_vendor_cmd_alloc_reply_skb failed",
__func__);
return -ENOMEM;
}
if (nla_put_u16(skb, COUNT_FROM_NETDEV,
adapter->hdd_stats.hddArpStats.txCount) ||
nla_put_u16(skb, COUNT_TO_LOWER_MAC,
adapter->hdd_stats.hddArpStats.tx_host_fw_sent) ||
nla_put_u16(skb, RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hddArpStats.tx_fw_cnt) ||
nla_put_u16(skb, COUNT_TX_SUCCESS,
adapter->hdd_stats.hddArpStats.tx_ack_cnt) ||
nla_put_u16(skb, RSP_RX_COUNT_BY_LOWER_MAC,
adapter->hdd_stats.hddArpStats.rx_fw_cnt) ||
nla_put_u16(skb, RSP_RX_COUNT_BY_UPPER_MAC,
adapter->hdd_stats.hddArpStats.rxCount) ||
nla_put_u16(skb, RSP_COUNT_TO_NETDEV,
adapter->hdd_stats.hddArpStats.rxDelivered) ||
nla_put_u16(skb, RSP_COUNT_OUT_OF_ORDER_DROP,
adapter->hdd_stats.hddArpStats.rx_host_drop_reorder)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(skb);
return -EINVAL;
}
if (adapter->con_status)
nla_put_flag(skb, AP_LINK_ACTIVE);
if (adapter->dad)
nla_put_flag(skb, AP_LINK_DAD);
cfg80211_vendor_cmd_reply(skb);
return err;
}
static int wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_nud_stats(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#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
#ifdef WLAN_FEATURE_APFIND
/**
* __wlan_hdd_cfg80211_apfind_cmd() - set configuration to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to send APFIND configurations to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int __wlan_hdd_cfg80211_apfind_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
struct sme_ap_find_request_req apfind_req;
VOS_STATUS status;
int ret_val;
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
ENTER();
ret_val = wlan_hdd_validate_context(hdd_ctx);
if (ret_val)
return ret_val;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EPERM;
}
apfind_req.request_data_len = data_len;
apfind_req.request_data = data;
status = sme_apfind_set_cmd(&apfind_req);
if (VOS_STATUS_SUCCESS != status) {
ret_val = -EIO;
}
return ret_val;
}
/**
* wlan_hdd_cfg80211_apfind_cmd() - set configuration to firmware
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: pointer to apfind configuration data.
* @data_len: the length in byte of apfind data.
*
* This is called when wlan driver needs to send APFIND configurations to
* firmware.
*
* Return: An error code or 0 on success.
*/
static int wlan_hdd_cfg80211_apfind_cmd(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_apfind_cmd(wiphy, wdev, data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /* WLAN_FEATURE_APFIND */
/**
* __wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
__wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int status;
uint32_t features;
struct sk_buff *reply_skb = NULL;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return -EINVAL;
features = 0;
if (hdd_is_memdump_supported())
features |= WIFI_LOGGER_MEMORY_DUMP_SUPPORTED;
if (hdd_ctx->cfg_ini->wlanLoggingEnable &&
hdd_ctx->cfg_ini->enableFatalEvent &&
hdd_ctx->is_fatal_event_log_sup) {
features |= WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORTED;
features |= WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
}
reply_skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy,
sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN);
if (!reply_skb) {
hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
return -ENOMEM;
}
hddLog(LOG1, FL("Supported logger features: 0x%0x"), features);
if (nla_put_u32(reply_skb, QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED,
features)) {
hddLog(LOGE, FL("nla put fail"));
kfree_skb(reply_skb);
return -EINVAL;
}
return cfg80211_vendor_cmd_reply(reply_skb);
}
/**
* wlan_hdd_cfg80211_get_logger_supp_feature() - Get the wifi logger features
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
* @data: Pointer to the data to be passed via vendor interface
* @data_len:Length of the data to be passed
*
* This is called by userspace to know the supported logger features
*
* Return: Return the Success or Failure code.
*/
static int
wlan_hdd_cfg80211_get_logger_supp_feature(struct wiphy *wiphy,
struct wireless_dev *wdev,
const void *data, int data_len)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_logger_supp_feature(wiphy, wdev,
data, data_len);
vos_ssr_unprotect(__func__);
return ret;
}
const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] =
{
{
.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_firmware_roaming
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_nan_request
},
#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 WLAN_FEATURE_EXTSCAN
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_start
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_extscan_stop
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_VALID_CHANNELS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_extscan_get_valid_channels
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
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
},
#endif /* WLAN_FEATURE_EXTSCAN */
/*EXT TDLS*/
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_exttdls_enable
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_exttdls_disable
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_exttdls_get_status
},
{
.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_NO_DFS_FLAG,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_disable_dfs_channels
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_MAC_OUI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_set_spoofed_mac_oui
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_concurrency_matrix
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_fw_mem_dump
},
{
.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_WIFI_LOGGER_START,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_wifi_logger_start
},
{
.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|
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_wifi_info
},
{
.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_MONITOR_RSSI,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_monitor_rssi
},
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_offloaded_packets
},
#endif
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = wlan_hdd_cfg80211_get_link_properties
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_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
},
#ifdef WLAN_FEATURE_APFIND
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_APFIND,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_apfind_cmd
},
#endif /* WLAN_FEATURE_APFIND */
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_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
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_STATION,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV |
WIPHY_VENDOR_CMD_NEED_RUNNING,
.doit = hdd_cfg80211_get_station_cmd
},
{
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_logger_supp_feature
},
};
/* vendor specific events */
static const
struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
{
#ifdef FEATURE_WLAN_CH_AVOID
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY
},
#endif /* FEATURE_WLAN_CH_AVOID Index = 0*/
#ifdef WLAN_FEATURE_LINK_LAYER_STATS
{
/* Index = 1*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET
},
{
/* Index = 2*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET
},
{
/* Index = 3*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR
},
{
/* Index = 4*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS
},
{
/* Index = 5*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS
},
{
/* Index = 6*/
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS
},
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
#ifdef WLAN_FEATURE_EXTSCAN
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_START
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_STOP
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CACHED_RESULTS
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_RESULTS_AVAILABLE
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_FULL_SCAN_RESULT
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SCAN_EVENT
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_FOUND
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_SET_BSSID_HOTLIST
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_RESET_BSSID_HOTLIST
},
#endif /* WLAN_FEATURE_EXTSCAN */
/*EXT TDLS*/
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE
},
[QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP
},
[QCA_NL80211_VENDOR_SUBCMD_NAN_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_NAN
},
{
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO,
},
[QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI
},
[QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_HOTLIST_AP_LOST
},
[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_LINK_PROPERTIES_INDEX] = {
.vendor_id = QCA_NL80211_VENDOR_ID,
.subcmd = QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES,
},
};
/*
* FUNCTION: wlan_hdd_cfg80211_wiphy_alloc
* This function is called by hdd_wlan_startup()
* during initialization.
* This function is used to allocate wiphy structure.
*/
struct wiphy *wlan_hdd_cfg80211_wiphy_alloc(int priv_size)
{
struct wiphy *wiphy;
ENTER();
/*
* Create wiphy device
*/
wiphy = wiphy_new(&wlan_hdd_cfg80211_ops, priv_size);
if (!wiphy)
{
/* Print error and jump into err label and free the memory */
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wiphy init failed", __func__);
return NULL;
}
return wiphy;
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,4,0)) || \
defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT)
/**
* hdd_config_sched_scan_plans_to_wiphy() - configure sched scan plans to wiphy
* @wiphy: pointer to wiphy
* @config: pointer to config
*
* Return: None
*/
static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
hdd_config_t *config)
{
wiphy->max_sched_scan_plans = MAX_SCHED_SCAN_PLANS;
if (config->max_sched_scan_plan_interval)
wiphy->max_sched_scan_plan_interval =
config->max_sched_scan_plan_interval;
if (config->max_sched_scan_plan_iterations)
wiphy->max_sched_scan_plan_iterations =
config->max_sched_scan_plan_iterations;
}
#else
static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
hdd_config_t *config)
{
}
#endif
/*
* FUNCTION: wlan_hdd_cfg80211_update_band
* This function is called from the supplicant through a
* private ioctl to change the band value
*/
int wlan_hdd_cfg80211_update_band(struct wiphy *wiphy, eCsrBand eBand)
{
int i, j;
eNVChannelEnabledType channelEnabledState;
ENTER();
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++)
{
if (NULL == wiphy->bands[i])
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy->bands[i] is NULL, i = %d",
__func__, i);
continue;
}
for (j = 0; j < wiphy->bands[i]->n_channels; j++)
{
struct ieee80211_supported_band *band = wiphy->bands[i];
channelEnabledState = vos_nv_getChannelEnabledState(
band->channels[j].hw_value);
if (HDD_NL80211_BAND_2GHZ == i && eCSR_BAND_5G == eBand) // 5G only
{
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
else if (HDD_NL80211_BAND_5GHZ == i && eCSR_BAND_24 == eBand) // 2G only
{
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
if (NV_CHANNEL_DISABLE == channelEnabledState ||
NV_CHANNEL_INVALID == channelEnabledState)
{
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
}
else if (NV_CHANNEL_DFS == channelEnabledState)
{
band->channels[j].flags &= ~IEEE80211_CHAN_DISABLED;
band->channels[j].flags |= IEEE80211_CHAN_RADAR;
}
else
{
band->channels[j].flags &= ~(IEEE80211_CHAN_DISABLED
|IEEE80211_CHAN_RADAR);
}
}
}
return 0;
}
/**
* hdd_add_channel_switch_support()- Adds Channel Switch flag if supported
* @wiphy: Pointer to the wiphy.
*
* 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(struct wiphy *wiphy)
{
wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
wiphy->max_num_csa_counters = WLAN_HDD_MAX_NUM_CSA_COUNTERS;
}
#else
static inline
void hdd_add_channel_switch_support(struct wiphy *wiphy)
{
}
#endif
#if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
hdd_config_t *config = hdd_ctx->cfg_ini;
if (config->enableMacSpoofing != MAC_ADDR_SPOOFING_FW_HOST_ENABLE)
return;
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
}
#endif
/*
* FUNCTION: wlan_hdd_cfg80211_init
* This function is called by hdd_wlan_startup()
* during initialization.
* This function is used to initialize and register wiphy structure.
*/
int wlan_hdd_cfg80211_init(struct device *dev,
struct wiphy *wiphy,
hdd_config_t *pCfg
)
{
int i, j;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
ENTER();
/* Now bind the underlying wlan device with wiphy */
set_wiphy_dev(wiphy, dev);
wiphy->mgmt_stypes = wlan_hdd_txrx_stypes;
#ifndef CONFIG_ENABLE_LINUX_REG
/* the flag for the other case would be initialzed in
vos_init_wiphy_from_nv_bin */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
#else
wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY;
#endif
#endif
/* This will disable updating of NL channels from passive to
* active if a beacon is received on passive channel. */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
#else
wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS)
wiphy->wowlan = &wowlan_support_cfg80211_init;
#else
wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_MAGIC_PKT;
wiphy->wowlan.n_patterns = WOWL_MAX_PTRNS_ALLOWED;
wiphy->wowlan.pattern_min_len = 1;
wiphy->wowlan.pattern_max_len = WOWL_PTRN_MAX_SIZE;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME
| WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD
| WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL
| WIPHY_FLAG_OFFCHAN_TX;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
#else
wiphy->country_ie_pref = NL80211_COUNTRY_IE_IGNORE_CORE;
#endif
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
if (pCfg->isFastTransitionEnabled
#ifdef FEATURE_WLAN_LFR
|| pCfg->isFastRoamIniFeatureEnabled
#endif
#ifdef FEATURE_WLAN_ESE
|| pCfg->isEseIniFeatureEnabled
#endif
)
{
wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
}
#endif
#ifdef FEATURE_WLAN_TDLS
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS
| WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
#ifdef FEATURE_WLAN_SCAN_PNO
if (pCfg->configPNOScanSupport)
{
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
wiphy->max_sched_scan_ssids = SIR_PNO_MAX_SUPP_NETWORKS;
wiphy->max_match_sets = SIR_PNO_MAX_SUPP_NETWORKS;
wiphy->max_sched_scan_ie_len = SIR_MAC_MAX_IE_LENGTH;
}
#endif/*FEATURE_WLAN_SCAN_PNO*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
wiphy->features |= NL80211_FEATURE_HT_IBSS;
#endif
#ifdef CONFIG_ENABLE_LINUX_REG
/* even with WIPHY_FLAG_CUSTOM_REGULATORY,
driver can still register regulatory callback and
it will get regulatory settings in wiphy->band[], but
driver need to determine what to do with both
regulatory settings */
wiphy->reg_notifier = wlan_hdd_linux_reg_notifier;
#else
wiphy->reg_notifier = wlan_hdd_crda_reg_notifier;
#endif
wiphy->max_scan_ssids = MAX_SCAN_SSID;
wiphy->max_scan_ie_len = SIR_MAC_MAX_ADD_IE_LENGTH;
wiphy->max_acl_mac_addrs = MAX_ACL_MAC_ADDRESS;
/* Supports STATION & AD-HOC modes right now */
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_ADHOC)
| BIT(NL80211_IFTYPE_P2P_CLIENT)
| BIT(NL80211_IFTYPE_P2P_GO)
| BIT(NL80211_IFTYPE_AP)
| BIT(NL80211_IFTYPE_MONITOR);
if( pCfg->advertiseConcurrentOperation )
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
if( pCfg->enableMCC )
{
/* Currently, supports up to two channels */
wlan_hdd_iface_combination[0].num_different_channels = 2;
if( !pCfg->allowMCCGODiffBI )
wlan_hdd_iface_combination[0].beacon_int_infra_match = true;
}
wiphy->iface_combinations = wlan_hdd_iface_combination;
wiphy->n_iface_combinations = ARRAY_SIZE(wlan_hdd_iface_combination);
#endif
}
/* Before registering we need to update the ht capabilitied based
* on ini values*/
if( !pCfg->ShortGI20MhzEnable )
{
wlan_hdd_band_2_4_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20;
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_20;
}
if( !pCfg->ShortGI40MhzEnable )
{
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40;
}
if( !pCfg->nChannelBondingMode5GHz )
{
wlan_hdd_band_5_GHZ.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
}
/*
* In case of static linked driver at the time of driver unload,
* module exit doesn't happens. Module cleanup helps in cleaning
* of static memory.
* If driver load happens statically, at the time of driver unload,
* wiphy flags don't get reset because of static memory.
* It's better not to store channel in static memory.
*/
wiphy->bands[HDD_NL80211_BAND_2GHZ] = &wlan_hdd_band_2_4_GHZ;
wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels =
(struct ieee80211_channel *)vos_mem_malloc(sizeof(hdd_channels_2_4_GHZ));
if (wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not enough memory to allocate channels"));
return -ENOMEM;
}
vos_mem_copy(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels,
&hdd_channels_2_4_GHZ[0],
sizeof(hdd_channels_2_4_GHZ));
if (true == hdd_is_5g_supported(pHddCtx))
{
wiphy->bands[HDD_NL80211_BAND_5GHZ] = &wlan_hdd_band_5_GHZ;
wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels =
(struct ieee80211_channel *)vos_mem_malloc(sizeof(hdd_channels_5_GHZ));
if (wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not enough memory to allocate channels"));
vos_mem_free(wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels);
wiphy->bands[HDD_NL80211_BAND_2GHZ]->channels = NULL;
return -ENOMEM;
}
vos_mem_copy(wiphy->bands[HDD_NL80211_BAND_5GHZ]->channels,
&hdd_channels_5_GHZ[0],
sizeof(hdd_channels_5_GHZ));
}
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++)
{
if (NULL == wiphy->bands[i])
{
hddLog(VOS_TRACE_LEVEL_INFO,"%s: wiphy->bands[i] is NULL, i = %d",
__func__, i);
continue;
}
for (j = 0; j < wiphy->bands[i]->n_channels; j++)
{
struct ieee80211_supported_band *band = wiphy->bands[i];
if (HDD_NL80211_BAND_2GHZ == i && eCSR_BAND_5G == pCfg->nBandCapability) // 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
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
else if (HDD_NL80211_BAND_5GHZ == i && eCSR_BAND_24 == pCfg->nBandCapability) // 2G only
{
band->channels[j].flags |= IEEE80211_CHAN_DISABLED;
continue;
}
}
}
/*Initialise the supported cipher suite details*/
wiphy->cipher_suites = hdd_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(hdd_cipher_suites);
/*signal strength in mBm (100*dBm) */
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
wiphy->max_remain_on_channel_duration = 5000;
#endif
hdd_add_channel_switch_support(wiphy);
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);
hdd_config_sched_scan_plans_to_wiphy(wiphy, pCfg);
EXIT();
return 0;
}
/* In this function we are registering wiphy. */
int wlan_hdd_cfg80211_register(struct wiphy *wiphy)
{
ENTER();
/* Register our wiphy dev with cfg80211 */
if (0 > wiphy_register(wiphy))
{
/* print error */
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__);
return -EIO;
}
EXIT();
return 0;
}
/* In this function we are updating channel list when,
regulatory domain is FCC and country code is US.
Here In FCC standard 5GHz UNII-1 Bands are indoor only.
As per FCC smart phone is not a indoor device.
GO should not opeate on indoor channels */
void wlan_hdd_cfg80211_update_reg_info(struct wiphy *wiphy)
{
int j;
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
tANI_U8 defaultCountryCode[3] = SME_INVALID_COUNTRY_CODE;
//Default counrtycode from NV at the time of wiphy initialization.
if (eHAL_STATUS_SUCCESS != sme_GetDefaultCountryCodeFrmNv(pHddCtx->hHal,
&defaultCountryCode[0]))
{
hddLog(LOGE, FL("Failed to get default country code from NV"));
}
if ((defaultCountryCode[0]== 'U') && (defaultCountryCode[1]=='S'))
{
if (NULL == wiphy->bands[HDD_NL80211_BAND_5GHZ])
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy->bands[HDD_NL80211_BAND_5GHZ] is NULL",__func__ );
return;
}
for (j = 0; j < wiphy->bands[HDD_NL80211_BAND_5GHZ]->n_channels; j++)
{
struct ieee80211_supported_band *band = wiphy->bands[HDD_NL80211_BAND_5GHZ];
// Mark UNII -1 band channel as passive
if (WLAN_HDD_CHANNEL_IN_UNII_1_BAND(band->channels[j].center_freq))
band->channels[j].flags |= IEEE80211_CHAN_PASSIVE_SCAN;
}
}
}
/* This function registers for all frame which supplicant is interested in */
void wlan_hdd_cfg80211_register_frames(hdd_adapter_t* pAdapter)
{
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
/* Register for all P2P action, public action etc frames */
v_U16_t type = (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_ACTION << 4);
ENTER();
/* Register frame indication call back */
sme_register_mgmt_frame_ind_callback(hHal, hdd_indicate_mgmt_frame);
/* Right now we are registering these frame when driver is getting
initialized. Once we will move to 2.6.37 kernel, in which we have
frame register ops, we will move this code as a part of that */
/* GAS Initial Request */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_INITIAL_REQ, GAS_INITIAL_REQ_SIZE );
/* GAS Initial Response */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_INITIAL_RSP, GAS_INITIAL_RSP_SIZE );
/* GAS Comeback Request */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_COMEBACK_REQ, GAS_COMEBACK_REQ_SIZE );
/* GAS Comeback Response */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_COMEBACK_RSP, GAS_COMEBACK_RSP_SIZE );
/* P2P Public Action */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)P2P_PUBLIC_ACTION_FRAME,
P2P_PUBLIC_ACTION_FRAME_SIZE );
/* P2P Action */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)P2P_ACTION_FRAME,
P2P_ACTION_FRAME_SIZE );
/* WNM BSS Transition Request frame */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)WNM_BSS_ACTION_FRAME,
WNM_BSS_ACTION_FRAME_SIZE );
/* WNM-Notification */
sme_RegisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)WNM_NOTIFICATION_FRAME,
WNM_NOTIFICATION_FRAME_SIZE );
}
void wlan_hdd_cfg80211_deregister_frames(hdd_adapter_t* pAdapter)
{
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
/* Register for all P2P action, public action etc frames */
v_U16_t type = (SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_ACTION << 4);
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_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_INITIAL_REQ, GAS_INITIAL_REQ_SIZE );
/* GAS Initial Response */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_INITIAL_RSP, GAS_INITIAL_RSP_SIZE );
/* GAS Comeback Request */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_COMEBACK_REQ, GAS_COMEBACK_REQ_SIZE );
/* GAS Comeback Response */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)GAS_COMEBACK_RSP, GAS_COMEBACK_RSP_SIZE );
/* P2P Public Action */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)P2P_PUBLIC_ACTION_FRAME,
P2P_PUBLIC_ACTION_FRAME_SIZE );
/* P2P Action */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)P2P_ACTION_FRAME,
P2P_ACTION_FRAME_SIZE );
/* WNM-Notification */
sme_DeregisterMgmtFrame(hHal, pAdapter->sessionId, type,
(v_U8_t*)WNM_NOTIFICATION_FRAME,
WNM_NOTIFICATION_FRAME_SIZE );
}
#ifdef FEATURE_WLAN_WAPI
void wlan_hdd_cfg80211_set_key_wapi(hdd_adapter_t* pAdapter, u8 key_index,
const u8 *mac_addr, const u8 *key , int key_Len)
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
tCsrRoamSetKey setKey;
v_BOOL_t isConnected = TRUE;
int status = 0;
v_U32_t roamId= 0xFF;
tANI_U8 *pKeyPtr = NULL;
int n = 0;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s(%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
vos_mem_zero(&setKey, sizeof(tCsrRoamSetKey));
setKey.keyId = key_index; // Store Key ID
setKey.encType = eCSR_ENCRYPT_TYPE_WPI; // SET WAPI Encryption
setKey.keyDirection = eSIR_TX_RX; // Key Directionn both TX and RX
setKey.paeRole = 0 ; // the PAE role
if (!mac_addr || is_broadcast_ether_addr(mac_addr))
{
vos_set_macaddr_broadcast( (v_MACADDR_t *)setKey.peerMac );
}
else
{
isConnected = hdd_connIsConnected(pHddStaCtx);
vos_mem_copy(setKey.peerMac,&pHddStaCtx->conn_info.bssId,WNI_CFG_BSSID_LEN);
}
setKey.keyLength = key_Len;
pKeyPtr = setKey.Key;
memcpy( pKeyPtr, key, key_Len);
hddLog(VOS_TRACE_LEVEL_INFO,"%s: WAPI KEY LENGTH:0x%04x",
__func__, key_Len);
for (n = 0 ; n < key_Len; n++)
hddLog(VOS_TRACE_LEVEL_INFO, "%s WAPI KEY Data[%d]:%02x ",
__func__,n,setKey.Key[n]);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_SETTING_KEY;
if ( isConnected )
{
status= sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
}
if ( status != 0 )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"[%4d] sme_RoamSetKey returned ERROR status= %d",
__LINE__, status );
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
}
/* Need to clear any trace of key value in the memory.
* Thus zero out the memory even though it is local
* variable.
*/
vos_mem_zero(&setKey, sizeof(setKey));
}
#endif /* FEATURE_WLAN_WAPI*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
int wlan_hdd_cfg80211_alloc_new_beacon(hdd_adapter_t *pAdapter,
beacon_data_t **ppBeacon,
struct beacon_parameters *params)
#else
int wlan_hdd_cfg80211_alloc_new_beacon(hdd_adapter_t *pAdapter,
beacon_data_t **ppBeacon,
struct cfg80211_beacon_data *params,
int dtim_period)
#endif
{
int size;
beacon_data_t *beacon = NULL;
beacon_data_t *old = NULL;
int head_len, tail_len, proberesp_ies_len, assocresp_ies_len;
const u8 *head, *tail, *proberesp_ies, *assocresp_ies;
ENTER();
if (params->head && !params->head_len)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("head_len is NULL"));
return -EINVAL;
}
old = pAdapter->sessionCtx.ap.beacon;
if (!params->head && !old)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("session(%d) old and new heads points to NULL"),
pAdapter->sessionId);
return -EINVAL;
}
if (params->tail && !params->tail_len)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("tail_len is zero but tail is not NULL"));
return -EINVAL;
}
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,38))
/* Kernel 3.0 is not updating dtim_period for set beacon */
if (!params->dtim_period)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("dtim period is 0"));
return -EINVAL;
}
#endif
if (params->head)
{
head_len = params->head_len;
head = params->head;
} else
{
head_len = old->head_len;
head = old->head;
}
if (params->tail || !old)
{
tail_len = params->tail_len;
tail = params->tail;
} else
{
tail_len = old->tail_len;
tail = old->tail;
}
if (params->proberesp_ies || !old)
{
proberesp_ies_len = params->proberesp_ies_len;
proberesp_ies = params->proberesp_ies;
} else
{
proberesp_ies_len = old->proberesp_ies_len;
proberesp_ies = old->proberesp_ies;
}
if (params->assocresp_ies || !old)
{
assocresp_ies_len = params->assocresp_ies_len;
assocresp_ies = params->assocresp_ies;
} else
{
assocresp_ies_len = old->assocresp_ies_len;
assocresp_ies = old->assocresp_ies;
}
size = sizeof(beacon_data_t) + head_len + tail_len +
proberesp_ies_len + assocresp_ies_len;
beacon = kzalloc(size, GFP_KERNEL);
if( beacon == NULL )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Mem allocation for beacon failed"));
return -ENOMEM;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
if (params->dtim_period)
beacon->dtim_period = params->dtim_period;
else
beacon->dtim_period = old->dtim_period;
#else
if (dtim_period)
beacon->dtim_period = dtim_period;
else
beacon->dtim_period = old->dtim_period;
#endif
beacon->head = ((u8 *) beacon) + sizeof(beacon_data_t);
beacon->tail = beacon->head + head_len;
beacon->proberesp_ies = beacon->tail + tail_len;
beacon->assocresp_ies = beacon->proberesp_ies + proberesp_ies_len;
beacon->head_len = head_len;
beacon->tail_len = tail_len;
beacon->proberesp_ies_len = proberesp_ies_len;
beacon->assocresp_ies_len= assocresp_ies_len;
if (head && head_len)
memcpy(beacon->head, head, head_len);
if (tail && tail_len)
memcpy(beacon->tail, tail, tail_len);
if (proberesp_ies && proberesp_ies_len)
memcpy(beacon->proberesp_ies, proberesp_ies, proberesp_ies_len);
if (assocresp_ies && assocresp_ies_len)
memcpy(beacon->assocresp_ies, assocresp_ies, assocresp_ies_len);
*ppBeacon = beacon;
kfree(old);
return 0;
}
v_U8_t* wlan_hdd_cfg80211_get_ie_ptr(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
const v_U8_t *pIes,
#else
v_U8_t *pIes,
#endif
int length, v_U8_t eid)
{
int left = length;
v_U8_t *ptr = (v_U8_t *)pIes;
v_U8_t elem_id,elem_len;
while(left >= 2)
{
elem_id = ptr[0];
elem_len = ptr[1];
left -= 2;
if(elem_len > left)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("****Invalid IEs eid = %d elem_len=%d left=%d*****"),
eid,elem_len,left);
return NULL;
}
if (elem_id == eid)
{
return ptr;
}
left -= elem_len;
ptr += (elem_len + 2);
}
return NULL;
}
/* Check if rate is 11g rate or not */
static int wlan_hdd_rate_is_11g(u8 rate)
{
static const u8 gRateArray[8] = {12, 18, 24, 36, 48, 72, 96, 108}; /* actual rate * 2 */
u8 i;
for (i = 0; i < 8; i++)
{
if(rate == gRateArray[i])
return TRUE;
}
return FALSE;
}
/* Check for 11g rate and set proper 11g only mode */
static void wlan_hdd_check_11gmode(u8 *pIe, u8* require_ht,
u8* pCheckRatesfor11g, eSapPhyMode* pSapHw_mode)
{
u8 i, num_rates = pIe[0];
pIe += 1;
for ( i = 0; i < num_rates; i++)
{
if( *pCheckRatesfor11g && (TRUE == wlan_hdd_rate_is_11g(pIe[i] & RATE_MASK)))
{
/* If rate set have 11g rate than change the mode to 11G */
*pSapHw_mode = eSAP_DOT11_MODE_11g;
if (pIe[i] & BASIC_RATE_MASK)
{
/* If we have 11g rate as basic rate, it means mode
is 11g only mode.
*/
*pSapHw_mode = eSAP_DOT11_MODE_11g_ONLY;
*pCheckRatesfor11g = FALSE;
}
}
else if((BASIC_RATE_MASK | WLAN_BSS_MEMBERSHIP_SELECTOR_HT_PHY) == pIe[i])
{
*require_ht = TRUE;
}
}
return;
}
static void wlan_hdd_set_sapHwmode(hdd_adapter_t *pHostapdAdapter)
{
tsap_Config_t *pConfig = &pHostapdAdapter->sessionCtx.ap.sapConfig;
beacon_data_t *pBeacon = pHostapdAdapter->sessionCtx.ap.beacon;
struct ieee80211_mgmt *pMgmt_frame = (struct ieee80211_mgmt*)pBeacon->head;
u8 checkRatesfor11g = TRUE;
u8 require_ht = FALSE;
u8 *pIe=NULL;
pConfig->SapHw_mode= eSAP_DOT11_MODE_11b;
pIe = wlan_hdd_cfg80211_get_ie_ptr(&pMgmt_frame->u.beacon.variable[0],
pBeacon->head_len, WLAN_EID_SUPP_RATES);
if (pIe != NULL)
{
pIe += 1;
wlan_hdd_check_11gmode(pIe, &require_ht, &checkRatesfor11g,
&pConfig->SapHw_mode);
}
pIe = wlan_hdd_cfg80211_get_ie_ptr(pBeacon->tail, pBeacon->tail_len,
WLAN_EID_EXT_SUPP_RATES);
if (pIe != NULL)
{
pIe += 1;
wlan_hdd_check_11gmode(pIe, &require_ht, &checkRatesfor11g,
&pConfig->SapHw_mode);
}
if( pConfig->channel > 14 )
{
pConfig->SapHw_mode= eSAP_DOT11_MODE_11a;
}
pIe = wlan_hdd_cfg80211_get_ie_ptr(pBeacon->tail, pBeacon->tail_len,
WLAN_EID_HT_CAPABILITY);
if(pIe)
{
pConfig->SapHw_mode= eSAP_DOT11_MODE_11n;
if(require_ht)
pConfig->SapHw_mode= eSAP_DOT11_MODE_11n_ONLY;
}
}
static int wlan_hdd_add_ie(hdd_adapter_t* pHostapdAdapter, v_U8_t *genie,
v_U8_t *total_ielen, v_U8_t *oui, v_U8_t oui_size)
{
v_U16_t ielen = 0;
v_U8_t *pIe = NULL;
beacon_data_t *pBeacon = pHostapdAdapter->sessionCtx.ap.beacon;
pIe = wlan_hdd_get_vendor_oui_ie_ptr(oui, oui_size,
pBeacon->tail, pBeacon->tail_len);
if (pIe)
{
ielen = pIe[1] + 2;
if ((*total_ielen + ielen) <= MAX_GENIE_LEN)
{
vos_mem_copy(&genie[*total_ielen], pIe, ielen);
}
else
{
hddLog( VOS_TRACE_LEVEL_ERROR, "**Ie Length is too big***");
return -EINVAL;
}
*total_ielen += ielen;
}
return 0;
}
static void wlan_hdd_add_hostapd_conf_vsie(hdd_adapter_t* pHostapdAdapter,
v_U8_t *genie, v_U8_t *total_ielen)
{
beacon_data_t *pBeacon = pHostapdAdapter->sessionCtx.ap.beacon;
int left = pBeacon->tail_len;
v_U8_t *ptr = pBeacon->tail;
v_U8_t elem_id, elem_len;
v_U16_t ielen = 0;
if ( NULL == ptr || 0 == left )
return;
while (left >= 2)
{
elem_id = ptr[0];
elem_len = ptr[1];
left -= 2;
if (elem_len > left)
{
hddLog( VOS_TRACE_LEVEL_ERROR,
"****Invalid IEs eid = %d elem_len=%d left=%d*****",
elem_id, elem_len, left);
return;
}
if ((IE_EID_VENDOR == elem_id) && (elem_len >= WPS_OUI_TYPE_SIZE))
{
/* skipping the VSIE's which we don't want to include or
* it will be included by existing code
*/
if ((memcmp( &ptr[2], WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE) != 0 ) &&
#ifdef WLAN_FEATURE_WFD
(memcmp( &ptr[2], WFD_OUI_TYPE, WFD_OUI_TYPE_SIZE) != 0) &&
#endif
(memcmp( &ptr[2], WHITELIST_OUI_TYPE, WPA_OUI_TYPE_SIZE) != 0) &&
(memcmp( &ptr[2], BLACKLIST_OUI_TYPE, WPA_OUI_TYPE_SIZE) != 0) &&
(memcmp( &ptr[2], "\x00\x50\xf2\x02", WPA_OUI_TYPE_SIZE) != 0) &&
(memcmp( &ptr[2], WPA_OUI_TYPE, WPA_OUI_TYPE_SIZE) != 0) &&
(memcmp( &ptr[2], P2P_OUI_TYPE, P2P_OUI_TYPE_SIZE) != 0))
{
ielen = ptr[1] + 2;
if ((*total_ielen + ielen) <= MAX_GENIE_LEN)
{
vos_mem_copy(&genie[*total_ielen], ptr, ielen);
*total_ielen += ielen;
}
else
{
hddLog( VOS_TRACE_LEVEL_ERROR,
"IE Length is too big "
"IEs eid=%d elem_len=%d total_ie_lent=%d",
elem_id, elem_len, *total_ielen);
}
}
}
left -= elem_len;
ptr += (elem_len + 2);
}
return;
}
int wlan_hdd_cfg80211_update_apies(hdd_adapter_t *pHostapdAdapter)
{
v_U8_t *genie;
v_U8_t total_ielen = 0;
v_U8_t addIE[1] = {0};
int ret = 0;
beacon_data_t *pBeacon = NULL;
genie = vos_mem_malloc(MAX_GENIE_LEN);
if(genie == NULL) {
return -ENOMEM;
}
pBeacon = pHostapdAdapter->sessionCtx.ap.beacon;
if (0 != wlan_hdd_add_ie(pHostapdAdapter, genie,
&total_ielen, WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE))
{
hddLog(LOGE,
FL("Adding WPS IE failed"));
ret = -EINVAL;
goto done;
}
#ifdef WLAN_FEATURE_WFD
if (0 != wlan_hdd_add_ie(pHostapdAdapter, genie,
&total_ielen, WFD_OUI_TYPE, WFD_OUI_TYPE_SIZE))
{
hddLog(LOGE,
FL("Adding WFD IE failed"));
ret = -EINVAL;
goto done;
}
#endif
if (0 != wlan_hdd_add_ie(pHostapdAdapter, genie,
&total_ielen, P2P_OUI_TYPE, P2P_OUI_TYPE_SIZE))
{
hddLog(LOGE,
FL("Adding P2P IE failed"));
ret = -EINVAL;
goto done;
}
if (WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode)
{
wlan_hdd_add_hostapd_conf_vsie(pHostapdAdapter, genie, &total_ielen);
}
if (ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, genie, total_ielen, NULL,
eANI_BOOLEAN_FALSE)==eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA to CCM");
ret = -EINVAL;
goto done;
}
if (ccmCfgSetInt((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, 1,NULL,
test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE)
==eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG to CCM");
ret = -EINVAL;
goto done;
}
// Added for ProResp IE
if ((pBeacon->proberesp_ies != NULL) && (pBeacon->proberesp_ies_len != 0))
{
u16 rem_probe_resp_ie_len = pBeacon->proberesp_ies_len;
u8 probe_rsp_ie_len[3] = {0};
u8 counter = 0;
/* Check Probe Resp Length if it is greater then 255 then Store
Probe Resp IEs into WNI_CFG_PROBE_RSP_ADDNIE_DATA1 &
WNI_CFG_PROBE_RSP_ADDNIE_DATA2 CFG Variable As We are not able
Store More then 255 bytes into One Variable.
*/
while ((rem_probe_resp_ie_len > 0) && (counter < 3))
{
if (rem_probe_resp_ie_len > MAX_CFG_STRING_LEN)
{
probe_rsp_ie_len[counter++] = MAX_CFG_STRING_LEN;
rem_probe_resp_ie_len -= MAX_CFG_STRING_LEN;
}
else
{
probe_rsp_ie_len[counter++] = rem_probe_resp_ie_len;
rem_probe_resp_ie_len = 0;
}
}
rem_probe_resp_ie_len = 0;
if (probe_rsp_ie_len[0] > 0)
{
if (ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA1,
(tANI_U8*)&pBeacon->
proberesp_ies[rem_probe_resp_ie_len],
probe_rsp_ie_len[0], NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA1 to CCM");
ret = -EINVAL;
goto done;
}
rem_probe_resp_ie_len += probe_rsp_ie_len[0];
}
if (probe_rsp_ie_len[1] > 0)
{
if (ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA2,
(tANI_U8*)&pBeacon->
proberesp_ies[rem_probe_resp_ie_len],
probe_rsp_ie_len[1], NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA2 to CCM");
ret = -EINVAL;
goto done;
}
rem_probe_resp_ie_len += probe_rsp_ie_len[1];
}
if (probe_rsp_ie_len[2] > 0)
{
if (ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA3,
(tANI_U8*)&pBeacon->
proberesp_ies[rem_probe_resp_ie_len],
probe_rsp_ie_len[2], NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA3 to CCM");
ret = -EINVAL;
goto done;
}
rem_probe_resp_ie_len += probe_rsp_ie_len[2];
}
if (probe_rsp_ie_len[1] == 0 )
{
if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA2, (tANI_U8*)addIE, 0, NULL,
eANI_BOOLEAN_FALSE) )
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA2 to CCM");
}
}
if (probe_rsp_ie_len[2] == 0 )
{
if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA3, (tANI_U8*)addIE, 0, NULL,
eANI_BOOLEAN_FALSE) )
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA3 to CCM");
}
}
if (ccmCfgSetInt((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_FLAG, 1,NULL,
test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_FLAG to CCM");
ret = -EINVAL;
goto done;
}
}
else
{
// Reset WNI_CFG_PROBE_RSP Flags
wlan_hdd_reset_prob_rspies(pHostapdAdapter);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: No Probe Response IE received in set beacon",
__func__);
}
// Added for AssocResp IE
if ((pBeacon->assocresp_ies != NULL) && (pBeacon->assocresp_ies_len != 0))
{
if (ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_ASSOC_RSP_ADDNIE_DATA, (tANI_U8*)pBeacon->assocresp_ies,
pBeacon->assocresp_ies_len, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_ASSOC_RSP_ADDNIE_DATA to CCM");
ret = -EINVAL;
goto done;
}
if (ccmCfgSetInt((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_ASSOC_RSP_ADDNIE_FLAG, 1, NULL,
test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags) ?
eANI_BOOLEAN_TRUE : eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_ASSOC_RSP_ADDNIE_FLAG to CCM");
ret = -EINVAL;
goto done;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: No Assoc Response IE received in set beacon",
__func__);
if ( eHAL_STATUS_FAILURE == ccmCfgSetInt((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_ASSOC_RSP_ADDNIE_FLAG, 0, NULL,
eANI_BOOLEAN_FALSE) )
{
hddLog(LOGE,
"Could not pass on WNI_CFG_ASSOC_RSP_ADDNIE_FLAG to CCM");
}
}
done:
vos_mem_free(genie);
return ret;
}
/*
* FUNCTION: wlan_hdd_validate_operation_channel
* called by wlan_hdd_cfg80211_start_bss() and
* wlan_hdd_cfg80211_set_channel()
* This function validates whether given channel is part of valid
* channel list.
*/
VOS_STATUS wlan_hdd_validate_operation_channel(hdd_adapter_t *pAdapter,int channel)
{
v_U32_t num_ch = 0;
u8 valid_ch[WNI_CFG_VALID_CHANNEL_LIST_LEN];
u32 indx = 0;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
v_U8_t fValidChannel = FALSE, count = 0;
hdd_config_t *hdd_pConfig_ini= (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini;
num_ch = WNI_CFG_VALID_CHANNEL_LIST_LEN;
if ( hdd_pConfig_ini->sapAllowAllChannel)
{
/* Validate the channel */
for (count = RF_CHAN_1 ; count <= RF_CHAN_165 ; count++)
{
if ( channel == rfChannels[count].channelNum )
{
fValidChannel = TRUE;
break;
}
}
if (fValidChannel != TRUE)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, channel);
return VOS_STATUS_E_FAILURE;
}
}
else
{
if (0 != ccmCfgGetStr(hHal, WNI_CFG_VALID_CHANNEL_LIST,
valid_ch, &num_ch))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to get valid channel list", __func__);
return VOS_STATUS_E_FAILURE;
}
for (indx = 0; indx < num_ch; indx++)
{
if (channel == valid_ch[indx])
{
break;
}
}
if (indx >= num_ch)
{
if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
eCsrBand band;
unsigned int freq;
sme_GetFreqBand(hHal, &band);
if (eCSR_BAND_5G == band)
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38))
if (channel <= ARRAY_SIZE(hdd_channels_2_4_GHZ))
{
freq = ieee80211_channel_to_frequency(channel,
HDD_NL80211_BAND_2GHZ);
}
else
{
freq = ieee80211_channel_to_frequency(channel,
HDD_NL80211_BAND_5GHZ);
}
#else
freq = ieee80211_channel_to_frequency(channel);
#endif
if(WLAN_HDD_IS_SOCIAL_CHANNEL(freq))
return VOS_STATUS_SUCCESS;
}
}
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, channel);
return VOS_STATUS_E_FAILURE;
}
}
return VOS_STATUS_SUCCESS;
}
/**
* FUNCTION: __wlan_hdd_cfg80211_set_channel
* This function is used to set the channel number
*/
static int __wlan_hdd_cfg80211_set_channel( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type
)
{
hdd_adapter_t *pAdapter = NULL;
v_U32_t num_ch = 0;
int channel = 0;
int freq = chan->center_freq; /* freq is in MHZ */
hdd_context_t *pHddCtx;
int status;
ENTER();
if( NULL == dev )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Called with dev = NULL.", __func__);
return -ENODEV;
}
pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_CHANNEL, pAdapter->sessionId,
channel_type ));
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: device_mode = %s (%d) freq = %d", __func__,
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode, chan->center_freq);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
/*
* Do freq to chan conversion
* TODO: for 11a
*/
channel = ieee80211_frequency_to_channel(freq);
/* Check freq range */
if ((WNI_CFG_CURRENT_CHANNEL_STAMIN > channel) ||
(WNI_CFG_CURRENT_CHANNEL_STAMAX < channel))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Channel [%d] is outside valid range from %d to %d",
__func__, channel, WNI_CFG_CURRENT_CHANNEL_STAMIN,
WNI_CFG_CURRENT_CHANNEL_STAMAX);
return -EINVAL;
}
num_ch = WNI_CFG_VALID_CHANNEL_LIST_LEN;
if ((WLAN_HDD_SOFTAP != pAdapter->device_mode) &&
(WLAN_HDD_P2P_GO != pAdapter->device_mode))
{
if(VOS_STATUS_SUCCESS != wlan_hdd_validate_operation_channel(pAdapter,channel))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, channel);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: set channel to [%d] for device mode =%d",
__func__, channel,pAdapter->device_mode);
}
if( (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)
)
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
tCsrRoamProfile * pRoamProfile = &pWextState->roamProfile;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_IbssConnected == pHddStaCtx->conn_info.connState)
{
/* Link is up then return cant set channel*/
hddLog( VOS_TRACE_LEVEL_ERROR,
"%s: IBSS Associated, can't set the channel", __func__);
return -EINVAL;
}
num_ch = pRoamProfile->ChannelInfo.numOfChannels = 1;
pHddStaCtx->conn_info.operationChannel = channel;
pRoamProfile->ChannelInfo.ChannelList =
&pHddStaCtx->conn_info.operationChannel;
}
else if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
if(VOS_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(pAdapter,channel))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, channel);
return -EINVAL;
}
(WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->sapConfig.channel = channel;
}
else if ( WLAN_HDD_SOFTAP == pAdapter->device_mode )
{
hdd_config_t *cfg_param = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini;
/* If auto channel selection is configured as enable/ 1 then ignore
channel set by supplicant
*/
if ( cfg_param->apAutoChannelSelection )
{
(WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->sapConfig.channel =
AUTO_CHANNEL_SELECT;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: set channel to auto channel (0) for device mode =%s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
}
else
{
if(VOS_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(pAdapter,channel))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, channel);
return -EINVAL;
}
(WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->sapConfig.channel = channel;
}
}
}
else
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Invalid device mode failed to set valid channel", __func__);
return -EINVAL;
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_set_channel( struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type
)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_channel(wiphy, dev, chan, channel_type);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef DHCP_SERVER_OFFLOAD
void hdd_dhcp_server_offload_done(void *fw_dhcp_srv_offload_cb_context,
VOS_STATUS status)
{
hdd_adapter_t* adapter = (hdd_adapter_t*)fw_dhcp_srv_offload_cb_context;
ENTER();
if (NULL == adapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: adapter is NULL",__func__);
return;
}
adapter->dhcp_status.dhcp_offload_status = status;
vos_event_set(&adapter->dhcp_status.vos_event);
return;
}
/**
* wlan_hdd_set_dhcp_server_offload() - set dhcp server offload
* @hostapd_adapter: pointer to hostapd adapter.
* @re_init: flag set if api called post ssr
*
* Return: None
*/
VOS_STATUS wlan_hdd_set_dhcp_server_offload(hdd_adapter_t *hostapd_adapter,
bool re_init)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(hostapd_adapter);
sir_dhcp_srv_offload_info dhcp_srv_info;
tANI_U8 num_entries = 0;
tANI_U8 srv_ip[IPADDR_NUM_ENTRIES];
tANI_U8 num;
tANI_U32 temp;
VOS_STATUS ret;
ENTER();
if (!re_init) {
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return VOS_STATUS_E_INVAL;
}
/* Prepare the request to send to SME */
dhcp_srv_info = vos_mem_malloc(sizeof(*dhcp_srv_info));
if (NULL == dhcp_srv_info) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: could not allocate tDhcpSrvOffloadInfo!", __func__);
return VOS_STATUS_E_NOMEM;
}
vos_mem_zero(dhcp_srv_info, sizeof(*dhcp_srv_info));
dhcp_srv_info->bssidx = hostapd_adapter->sessionId;
dhcp_srv_info->dhcp_srv_offload_enabled = TRUE;
dhcp_srv_info->dhcp_client_num = hdd_ctx->cfg_ini->dhcp_max_num_clients;
dhcp_srv_info->start_lsb = hdd_ctx->cfg_ini->dhcp_start_lsb;
dhcp_srv_info->dhcp_offload_callback = hdd_dhcp_server_offload_done;
dhcp_srv_info->dhcp_server_offload_cb_context = hostapd_adapter;
hdd_string_to_u8_array(hdd_ctx->cfg_ini->dhcp_srv_ip,
srv_ip,
&num_entries,
IPADDR_NUM_ENTRIES, ".", false);
if (num_entries != IPADDR_NUM_ENTRIES) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: incorrect IP address (%s) assigned for DHCP server!",
__func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
vos_mem_free(dhcp_srv_info);
return VOS_STATUS_E_FAILURE;
}
if ((srv_ip[0] >= 224) && (srv_ip[0] <= 239)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: invalid IP address (%s)! It could NOT be multicast IP address!",
__func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
vos_mem_free(dhcp_srv_info);
return VOS_STATUS_E_FAILURE;
}
if (srv_ip[IPADDR_NUM_ENTRIES-1] >= DHCP_START_POOL_ADDRESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: invalid IP address (%s)! The last field must be less than 100!",
__func__, hdd_ctx->cfg_ini->dhcp_srv_ip);
vos_mem_free(dhcp_srv_info);
return VOS_STATUS_E_FAILURE;
}
for (num = 0; num < num_entries; num++) {
temp = srv_ip[num];
dhcp_srv_info->dhcp_srv_ip |= (temp << (8 * num));
}
if (eHAL_STATUS_SUCCESS !=
sme_set_dhcp_srv_offload(hdd_ctx->hHal, dhcp_srv_info)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_set_dhcp_srv_offload fail!", __func__);
vos_mem_free(dhcp_srv_info);
return VOS_STATUS_E_FAILURE;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: enable DHCP Server offload successfully!", __func__);
vos_mem_free(dhcp_srv_info);
return 0;
}
#endif /* DHCP_SERVER_OFFLOAD */
/*
* hdd_modify_indoor_channel_state_flags() - modify wiphy flags and cds state
* @wiphy_chan: wiphy channel number
* @rfChannel: channel hw value
* @disable: Disable/enable the flags
* @hdd_ctx: The HDD context handler
*
* Modify wiphy flags and cds state if channel is indoor.
*
* Return: void
*/
void hdd_modify_indoor_channel_state_flags(struct ieee80211_channel *wiphy_chan,
v_U32_t rfChannel, bool disable, hdd_context_t *hdd_ctx)
{
v_U32_t channelLoop;
eRfChannels channelEnum = INVALID_RF_CHANNEL;
for (channelLoop = 0; channelLoop <= RF_CHAN_165; channelLoop++) {
if (rfChannels[channelLoop].channelNum == rfChannel) {
channelEnum = (eRfChannels)channelLoop;
break;
}
}
if (INVALID_RF_CHANNEL == channelEnum)
return;
if (disable) {
if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) {
wiphy_chan->flags |=
IEEE80211_CHAN_DISABLED;
regChannels[channelEnum].enabled =
NV_CHANNEL_DISABLE;
hddLog(VOS_TRACE_LEVEL_INFO, "Channel: %d marked as DISABLE",
channelEnum);
}
} else {
if (wiphy_chan->flags & IEEE80211_CHAN_INDOOR_ONLY) {
wiphy_chan->flags &=
~IEEE80211_CHAN_DISABLED;
/*
* Indoor channels are marked as DFS
* during regulatory processing
*/
if ((wiphy_chan->flags & (IEEE80211_CHAN_RADAR |
IEEE80211_CHAN_PASSIVE_SCAN)) ||
((hdd_ctx->cfg_ini->indoor_channel_support == false)
&& (wiphy_chan->flags &
IEEE80211_CHAN_INDOOR_ONLY))) {
regChannels[channelEnum].enabled = NV_CHANNEL_DFS;
hddLog(VOS_TRACE_LEVEL_INFO, "Channel: %d marked as DFS",
channelEnum);
} else {
regChannels[channelEnum].enabled =
NV_CHANNEL_ENABLE;
hddLog(VOS_TRACE_LEVEL_INFO, "Channel: %d marked as ENABLE",
channelEnum);
}
}
}
}
void hdd_update_indoor_channel(hdd_context_t *hdd_ctx, bool disable)
{
int band_num;
int chan_num;
v_U32_t rfChannel;
struct ieee80211_channel *wiphy_chan;
struct wiphy *wiphy;
ENTER();
hddLog(VOS_TRACE_LEVEL_INFO, "disable: %d", disable);
wiphy = hdd_ctx->wiphy;
for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS; band_num++) {
if (wiphy->bands[band_num] == NULL)
continue;
for (chan_num = 0;
chan_num < wiphy->bands[band_num]->n_channels;
chan_num++) {
wiphy_chan =
&(wiphy->bands[band_num]->channels[chan_num]);
rfChannel = wiphy->bands[band_num]->channels[chan_num].hw_value;
hdd_modify_indoor_channel_state_flags(wiphy_chan, rfChannel,
disable, hdd_ctx);
}
}
EXIT();
}
int wlan_hdd_disconnect( hdd_adapter_t *pAdapter, u16 reason )
{
eHalStatus status;
int result = 0;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
long ret;
eConnectionState prev_conn_state;
uint32_t wait_time = WLAN_WAIT_TIME_DISCONNECT;
ENTER();
/* Indicate sme of disconnect so that in progress connection or preauth
* can be aborted
*/
sme_abortConnection(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId);
pHddCtx->isAmpAllowed = VOS_TRUE;
/* Need to apply spin lock before decreasing active sessions
* as there can be chance for double decrement if context switch
* Calls hdd_DisConnectHandler.
*/
prev_conn_state = pHddStaCtx->conn_info.connState;
spin_lock_bh(&pAdapter->lock_for_active_session);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
{
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
}
hdd_connSetConnectionState( pHddStaCtx, eConnectionState_Disconnecting );
spin_unlock_bh(&pAdapter->lock_for_active_session);
vos_flush_delayed_work(&pHddCtx->ecsa_chan_change_work);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL( "Set HDD connState to eConnectionState_Disconnecting" ));
INIT_COMPLETION(pAdapter->disconnect_comp_var);
/*
* stop tx queues before deleting STA/BSS context from the firmware.
* tx has to be disabled because the firmware can get busy dropping
* the tx frames after BSS/STA has been deleted and will not send
* back a response resulting in WDI timeout
*/
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
netif_carrier_off(pAdapter->dev);
wlan_hdd_check_and_stop_mon(pAdapter, true);
/*issue disconnect*/
status = sme_RoamDisconnect( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, reason);
if((eHAL_STATUS_CMD_NOT_QUEUED == status) &&
prev_conn_state != eConnectionState_Connecting)
{
hddLog(LOG1,
FL("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;
goto wait_for_disconnect;
}
/*
* 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.
*/
else if(eHAL_STATUS_CMD_NOT_QUEUED == status)
{
hddLog(LOG1,
FL("Already disconnected or connect was in sme/roam pending list and removed by disconnect"));
}
else if ( 0 != status )
{
hddLog(LOGE,
FL("csrRoamDisconnect failure, returned %d"),
(int)status);
result = -EINVAL;
goto disconnected;
}
wait_for_disconnect:
ret = wait_for_completion_timeout(&pAdapter->disconnect_comp_var,
msecs_to_jiffies(wait_time));
if (!ret && (eHAL_STATUS_CMD_NOT_QUEUED != status))
{
hddLog(LOGE,
"%s: Failed to disconnect, timed out", __func__);
result = -ETIMEDOUT;
}
disconnected:
hddLog(LOG1,
FL("Set HDD connState to eConnectionState_NotConnected"));
pHddStaCtx->conn_info.connState = 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
*/
hddLog(LOG1, FL("Send disconnected event to userspace"));
wlan_hdd_cfg80211_indicate_disconnect(pAdapter->dev, true,
WLAN_REASON_UNSPECIFIED);
#endif
EXIT();
return result;
}
void hdd_check_and_disconnect_sta_on_invalid_channel(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *sta_adapter;
tANI_U8 sta_chan;
sta_chan = hdd_get_operating_channel(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!sta_chan) {
hddLog(LOG1, FL("STA not connected"));
return;
}
hddLog(LOG1, FL("STA connected on chan %hu"), sta_chan);
if (sme_IsChannelValid(hdd_ctx->hHal, sta_chan)) {
hddLog(LOG1, FL("STA connected on chan %hu and it is valid"),
sta_chan);
return;
}
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!sta_adapter) {
hddLog(LOG1, FL("STA adapter doesn't exist"));
return;
}
hddLog(LOG1, FL("chan %hu not valid, issue disconnect"), sta_chan);
/* Issue Disconnect request */
wlan_hdd_disconnect(sta_adapter, eCSR_DISCONNECT_REASON_DEAUTH);
}
int wlan_hdd_restore_channels(hdd_context_t *hdd_ctx)
{
struct hdd_cache_channels *cache_chann;
struct wiphy *wiphy;
int freq, status, rfChannel;
int i, band_num, channel_num;
struct ieee80211_channel *wiphy_channel;
ENTER();
if (!hdd_ctx) {
hddLog(VOS_TRACE_LEVEL_FATAL, "HDD Context is NULL");
return -EINVAL;
}
wiphy = hdd_ctx->wiphy;
mutex_lock(&hdd_ctx->cache_channel_lock);
cache_chann = hdd_ctx->original_channels;
if (!cache_chann || !cache_chann->num_channels) {
hddLog(VOS_TRACE_LEVEL_INFO,
"%s channel list is NULL or num channels are zero",
__func__);
mutex_unlock(&hdd_ctx->cache_channel_lock);
return -EINVAL;
}
for (i = 0; i < cache_chann->num_channels; i++) {
status = hdd_wlan_get_freq(
cache_chann->channel_info[i].channel_num,
&freq);
for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS;
band_num++) {
if (!wiphy->bands[band_num])
continue;
for (channel_num = 0; channel_num <
wiphy->bands[band_num]->n_channels;
channel_num++) {
wiphy_channel = &(wiphy->bands[band_num]->
channels[channel_num]);
if (wiphy_channel->center_freq == freq) {
rfChannel = wiphy_channel->hw_value;
/*
*Restore the orginal states
*of the channels
*/
vos_nv_set_channel_state(
rfChannel,
cache_chann->
channel_info[i].reg_status);
wiphy_channel->flags =
cache_chann->
channel_info[i].wiphy_status;
hddLog(VOS_TRACE_LEVEL_DEBUG,
"Restore channel %d reg_stat %d wiphy_stat 0x%x",
cache_chann->
channel_info[i].channel_num,
cache_chann->
channel_info[i].reg_status,
wiphy_channel->flags);
break;
}
}
if (channel_num < wiphy->bands[band_num]->n_channels)
break;
}
}
mutex_unlock(&hdd_ctx->cache_channel_lock);
status = sme_update_channel_list((tpAniSirGlobal)hdd_ctx->hHal);
if (status)
hddLog(VOS_TRACE_LEVEL_ERROR, "Can't Restore channel list");
EXIT();
return 0;
}
int wlan_hdd_disable_channels(hdd_context_t *hdd_ctx)
{
struct hdd_cache_channels *cache_chann;
struct wiphy *wiphy;
int freq, status, rfChannel;
int i, band_num, band_ch_num;
struct ieee80211_channel *wiphy_channel;
if (!hdd_ctx) {
hddLog(VOS_TRACE_LEVEL_FATAL, "HDD Context is NULL");
return -EINVAL;
}
wiphy = hdd_ctx->wiphy;
mutex_lock(&hdd_ctx->cache_channel_lock);
cache_chann = hdd_ctx->original_channels;
if (!cache_chann || !cache_chann->num_channels) {
hddLog(VOS_TRACE_LEVEL_INFO,
"%s channel list is NULL or num channels are zero",
__func__);
mutex_unlock(&hdd_ctx->cache_channel_lock);
return -EINVAL;
}
for (i = 0; i < cache_chann->num_channels; i++) {
status = hdd_wlan_get_freq(
cache_chann->channel_info[i].channel_num,
&freq);
for (band_num = 0; band_num < HDD_NUM_NL80211_BANDS;
band_num++) {
if (!wiphy->bands[band_num])
continue;
for (band_ch_num = 0; band_ch_num <
wiphy->bands[band_num]->n_channels;
band_ch_num++) {
wiphy_channel = &(wiphy->bands[band_num]->
channels[band_ch_num]);
if (wiphy_channel->center_freq == freq) {
rfChannel = wiphy_channel->hw_value;
/*
* Cache the current states of
* the channels
*/
cache_chann->
channel_info[i].reg_status =
vos_nv_getChannelEnabledState(
rfChannel);
cache_chann->
channel_info[i].wiphy_status =
wiphy_channel->flags;
hddLog(VOS_TRACE_LEVEL_INFO,
"Disable channel %d reg_stat %d wiphy_stat 0x%x",
cache_chann->
channel_info[i].channel_num,
cache_chann->
channel_info[i].reg_status,
wiphy_channel->flags);
vos_nv_set_channel_state(
rfChannel,
NV_CHANNEL_DISABLE);
wiphy_channel->flags |=
IEEE80211_CHAN_DISABLED;
break;
}
}
if (band_ch_num < wiphy->bands[band_num]->n_channels)
break;
}
}
mutex_unlock(&hdd_ctx->cache_channel_lock);
sme_update_channel_list((tpAniSirGlobal)hdd_ctx->hHal);
return 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
struct beacon_parameters *params)
#else
static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
struct cfg80211_beacon_data *params,
const u8 *ssid, size_t ssid_len,
enum nl80211_hidden_ssid hidden_ssid,
v_U8_t auth_type)
#endif
{
tsap_Config_t *pConfig;
beacon_data_t *pBeacon = NULL;
struct ieee80211_mgmt *pMgmt_frame;
v_U8_t *pIe=NULL;
v_U16_t capab_info;
eCsrAuthType RSNAuthType;
eCsrEncryptionType RSNEncryptType;
eCsrEncryptionType mcRSNEncryptType;
int status = VOS_STATUS_SUCCESS, ret = 0;
tpWLAN_SAPEventCB pSapEventCallback;
hdd_hostapd_state_t *pHostapdState;
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pHostapdAdapter);
struct qc_mac_acl_entry *acl_entry = NULL;
hdd_config_t *iniConfig;
v_SINT_t i;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter);
hdd_adapter_t *sta_adapter;
tSmeConfigParams *psmeConfig;
v_BOOL_t MFPCapable = VOS_FALSE;
v_BOOL_t MFPRequired = VOS_FALSE;
v_BOOL_t sapEnable11AC =
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->sapEnable11AC;
u_int16_t prev_rsn_length = 0;
ENTER();
wlan_hdd_tdls_disable_offchan_and_teardown_links(pHddCtx);
/*
* For STA+SAP concurrency support from GUI, first STA connection gets
* triggered and while it is in progress, SAP start also comes up.
* Once STA association is successful, STA connect event is sent to
* kernel which gets queued in kernel workqueue and supplicant won't
* process M1 received from AP and send M2 until this NL80211_CONNECT
* event is received. Workqueue is not scheduled as RTNL lock is already
* taken by hostapd thread which has issued start_bss command to driver.
* Driver cannot complete start_bss as the pending command at the head
* of the SME command pending list is hw_mode_update for STA session
* which cannot be processed as SME is in WAITforKey state for STA
* interface. The start_bss command for SAP interface is queued behind
* the hw_mode_update command and so it cannot be processed until
* hw_mode_update command is processed. This is causing a deadlock so
* disconnect the STA interface first if connection or key exchange is
* in progress and then start SAP interface.
*/
sta_adapter = hdd_get_sta_connection_in_progress(pHddCtx);
if (sta_adapter) {
hddLog(LOG1, FL("Disconnecting STA with session id: %d"),
sta_adapter->sessionId);
wlan_hdd_disconnect(sta_adapter, eCSR_DISCONNECT_REASON_DEAUTH);
}
iniConfig = pHddCtx->cfg_ini;
/* Mark the indoor channel (passive) to disable */
if (iniConfig->disable_indoor_channel &&
pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP) {
hdd_update_indoor_channel(pHddCtx, true);
if (!VOS_IS_STATUS_SUCCESS(
sme_update_channel_list((tpAniSirGlobal)pHddCtx->hHal))) {
hdd_update_indoor_channel(pHddCtx, false);
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
FL("Can't start BSS: update channel list failed"));
ret = eHAL_STATUS_FAILURE;
goto tdls_enable;
}
/* check if STA is on indoor channel */
if (hdd_is_sta_sap_scc_allowed_on_dfs_chan(pHddCtx))
hdd_check_and_disconnect_sta_on_invalid_channel(pHddCtx);
}
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter);
pConfig = &pHostapdAdapter->sessionCtx.ap.sapConfig;
pBeacon = pHostapdAdapter->sessionCtx.ap.beacon;
pMgmt_frame = (struct ieee80211_mgmt*)pBeacon->head;
pConfig->beacon_int = pMgmt_frame->u.beacon.beacon_int;
//channel is already set in the set_channel Call back
//pConfig->channel = pCommitConfig->channel;
/*Protection parameter to enable or disable*/
pConfig->protEnabled =
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->apProtEnabled;
pConfig->dtim_period = pBeacon->dtim_period;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"****pConfig->dtim_period=%d***",
pConfig->dtim_period);
if (pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP)
{
pIe = wlan_hdd_cfg80211_get_ie_ptr(pBeacon->tail, pBeacon->tail_len,
WLAN_EID_COUNTRY);
if(memcmp(pHddCtx->cfg_ini->apCntryCode, CFG_AP_COUNTRY_CODE_DEFAULT, 3) != 0)
{
tANI_BOOLEAN restartNeeded;
pConfig->ieee80211d = 1;
vos_mem_copy(pConfig->countryCode, pHddCtx->cfg_ini->apCntryCode, 3);
sme_setRegInfo(hHal, pConfig->countryCode);
sme_ResetCountryCodeInformation(hHal, &restartNeeded);
}
else if(pIe)
{
tANI_BOOLEAN restartNeeded;
pConfig->ieee80211d = 1;
vos_mem_copy(pConfig->countryCode, &pIe[2], 3);
sme_setRegInfo(hHal, pConfig->countryCode);
sme_ResetCountryCodeInformation(hHal, &restartNeeded);
}
else
{
pConfig->ieee80211d = 0;
}
/*
* If auto channel is configured i.e. channel is 0,
* so skip channel validation.
*/
if( AUTO_CHANNEL_SELECT != pConfig->channel )
{
if(VOS_STATUS_SUCCESS != wlan_hdd_validate_operation_channel(pHostapdAdapter,pConfig->channel))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Channel [%d]", __func__, pConfig->channel);
ret = -EINVAL;
goto error;
}
pConfig->user_config_channel = pConfig->channel;
}
else
{
if(1 != pHddCtx->is_dynamic_channel_range_set)
{
hdd_config_t *hdd_pConfig= (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini;
WLANSAP_SetChannelRange(hHal, hdd_pConfig->apStartChannelNum,
hdd_pConfig->apEndChannelNum,hdd_pConfig->apOperatingBand);
}
pHddCtx->is_dynamic_channel_range_set = 0;
pConfig->user_config_channel = SAP_DEFAULT_24GHZ_CHANNEL;
}
}
else
{
pConfig->ieee80211d = 0;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
if (params->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
pConfig->authType = eSAP_OPEN_SYSTEM;
else if (params->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
pConfig->authType = eSAP_SHARED_KEY;
else
pConfig->authType = eSAP_AUTO_SWITCH;
#else
if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
pConfig->authType = eSAP_OPEN_SYSTEM;
else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
pConfig->authType = eSAP_SHARED_KEY;
else
pConfig->authType = eSAP_AUTO_SWITCH;
#endif
capab_info = pMgmt_frame->u.beacon.capab_info;
pConfig->privacy = (pMgmt_frame->u.beacon.capab_info &
WLAN_CAPABILITY_PRIVACY) ? VOS_TRUE : VOS_FALSE;
#ifdef SAP_AUTH_OFFLOAD
/* In case of sap offload, hostapd.conf is configuted with open mode and
* security is configured from ini file. Due to open mode in hostapd.conf
* privacy bit is set to false which will result in not sending,
* data packets as encrypted.
* If enable_sap_auth_offload is enabled in ini and
* sap_auth_offload_sec_type is type of WPA2-PSK,
* driver will set privacy bit to 1.
*/
if (pHddCtx->cfg_ini->enable_sap_auth_offload &&
pHddCtx->cfg_ini->sap_auth_offload_sec_type)
pConfig->privacy = VOS_TRUE;
#endif
(WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uPrivacy = pConfig->privacy;
/*Set wps station to configured*/
pIe = wlan_hdd_get_wps_ie_ptr(pBeacon->tail, pBeacon->tail_len);
if(pIe)
{
if(pIe[1] < (2 + WPS_OUI_TYPE_SIZE))
{
hddLog( VOS_TRACE_LEVEL_ERROR, "**Wps Ie Length is too small***");
ret = -EINVAL;
goto error;
}
else if(memcmp(&pIe[2], WPS_OUI_TYPE, WPS_OUI_TYPE_SIZE) == 0)
{
hddLog( VOS_TRACE_LEVEL_INFO, "** WPS IE(len %d) ***", (pIe[1]+2));
/* Check 15 bit of WPS IE as it contain information for wps state
* WPS state
*/
if(SAP_WPS_ENABLED_UNCONFIGURED == pIe[15])
{
pConfig->wps_state = SAP_WPS_ENABLED_UNCONFIGURED;
} else if(SAP_WPS_ENABLED_CONFIGURED == pIe[15])
{
pConfig->wps_state = SAP_WPS_ENABLED_CONFIGURED;
}
}
}
else
{
pConfig->wps_state = SAP_WPS_DISABLED;
}
pConfig->fwdWPSPBCProbeReq = 1; // Forward WPS PBC probe request frame up
pConfig->RSNEncryptType = eCSR_ENCRYPT_TYPE_NONE;
pConfig->mcRSNEncryptType = eCSR_ENCRYPT_TYPE_NONE;
(WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->ucEncryptType =
eCSR_ENCRYPT_TYPE_NONE;
pConfig->RSNWPAReqIELength = 0;
memset(&pConfig->RSNWPAReqIE[0], 0, sizeof(pConfig->RSNWPAReqIE));
pIe = wlan_hdd_cfg80211_get_ie_ptr(pBeacon->tail, pBeacon->tail_len,
WLAN_EID_RSN);
if(pIe && pIe[1])
{
pConfig->RSNWPAReqIELength = pIe[1] + 2;
if (pConfig->RSNWPAReqIELength <= sizeof(pConfig->RSNWPAReqIE))
memcpy(&pConfig->RSNWPAReqIE[0], pIe,
pConfig->RSNWPAReqIELength);
else
hddLog(LOGE, "RSNWPA IE MAX Length exceeded; length =%d",
pConfig->RSNWPAReqIELength);
/* The actual processing may eventually be more extensive than
* this. Right now, just consume any PMKIDs that are sent in
* by the app.
* */
status = hdd_softap_unpackIE(
vos_get_context( VOS_MODULE_ID_SME, pVosContext),
&RSNEncryptType,
&mcRSNEncryptType,
&RSNAuthType,
&MFPCapable,
&MFPRequired,
pConfig->RSNWPAReqIE[1]+2,
pConfig->RSNWPAReqIE);
if( VOS_STATUS_SUCCESS == status )
{
/* Now copy over all the security attributes you have
* parsed out
* */
pConfig->RSNEncryptType = RSNEncryptType; // Use the cipher type in the RSN IE
pConfig->mcRSNEncryptType = mcRSNEncryptType;
(WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->ucEncryptType
= RSNEncryptType;
hddLog( LOG1, FL("CSR AuthType = %d, "
"EncryptionType = %d mcEncryptionType = %d"),
RSNAuthType, RSNEncryptType, mcRSNEncryptType);
}
}
pIe = wlan_hdd_get_vendor_oui_ie_ptr(WPA_OUI_TYPE, WPA_OUI_TYPE_SIZE,
pBeacon->tail, pBeacon->tail_len);
if(pIe && pIe[1] && (pIe[0] == DOT11F_EID_WPA))
{
if (pConfig->RSNWPAReqIE[0])
{
/*Mixed mode WPA/WPA2*/
prev_rsn_length = pConfig->RSNWPAReqIELength;
pConfig->RSNWPAReqIELength += pIe[1] + 2;
if (pConfig->RSNWPAReqIELength <=
(sizeof(pConfig->RSNWPAReqIE) - prev_rsn_length))
memcpy(&pConfig->RSNWPAReqIE[0] + prev_rsn_length, pIe,
pIe[1] + 2);
else
hddLog(LOGE, "RSNWPA IE MAX Length exceeded; length =%d",
pConfig->RSNWPAReqIELength);
}
else
{
pConfig->RSNWPAReqIELength = pIe[1] + 2;
if (pConfig->RSNWPAReqIELength <= sizeof(pConfig->RSNWPAReqIE))
memcpy(&pConfig->RSNWPAReqIE[0], pIe,
pConfig->RSNWPAReqIELength);
else
hddLog(LOGE, "RSNWPA IE MAX Length exceeded; length =%d",
pConfig->RSNWPAReqIELength);
status = hdd_softap_unpackIE(
vos_get_context( VOS_MODULE_ID_SME, pVosContext),
&RSNEncryptType,
&mcRSNEncryptType,
&RSNAuthType,
&MFPCapable,
&MFPRequired,
pConfig->RSNWPAReqIE[1]+2,
pConfig->RSNWPAReqIE);
if( VOS_STATUS_SUCCESS == status )
{
/* Now copy over all the security attributes you have
* parsed out
* */
pConfig->RSNEncryptType = RSNEncryptType; // Use the cipher type in the RSN IE
pConfig->mcRSNEncryptType = mcRSNEncryptType;
(WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->ucEncryptType
= RSNEncryptType;
hddLog( LOG1, FL("CSR AuthType = %d, "
"EncryptionType = %d mcEncryptionType = %d"),
RSNAuthType, RSNEncryptType, mcRSNEncryptType);
}
}
}
if (pConfig->RSNWPAReqIELength > sizeof(pConfig->RSNWPAReqIE)) {
hddLog( VOS_TRACE_LEVEL_ERROR, "**RSNWPAReqIELength is too large***");
ret = -EINVAL;
goto error;
}
pConfig->SSIDinfo.ssidHidden = VOS_FALSE;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
if (params->ssid != NULL)
{
memcpy(pConfig->SSIDinfo.ssid.ssId, params->ssid, params->ssid_len);
pConfig->SSIDinfo.ssid.length = params->ssid_len;
if (params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
pConfig->SSIDinfo.ssidHidden = VOS_TRUE;
}
#else
if (ssid != NULL)
{
memcpy(pConfig->SSIDinfo.ssid.ssId, ssid, ssid_len);
pConfig->SSIDinfo.ssid.length = ssid_len;
if (hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
pConfig->SSIDinfo.ssidHidden = VOS_TRUE;
}
#endif
vos_mem_copy(pConfig->self_macaddr.bytes,
pHostapdAdapter->macAddressCurrent.bytes, sizeof(v_MACADDR_t));
/* default value */
pConfig->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED;
pConfig->num_accept_mac = 0;
pConfig->num_deny_mac = 0;
pIe = wlan_hdd_get_vendor_oui_ie_ptr(BLACKLIST_OUI_TYPE, WPA_OUI_TYPE_SIZE,
pBeacon->tail, pBeacon->tail_len);
/* pIe for black list is following form:
type : 1 byte
length : 1 byte
OUI : 4 bytes
acl type : 1 byte
no of mac addr in black list: 1 byte
list of mac_acl_entries: variable, 6 bytes per mac address + sizeof(int) for vlan id
*/
if ((pIe != NULL) && (pIe[1] != 0))
{
pConfig->SapMacaddr_acl = pIe[6];
pConfig->num_deny_mac = pIe[7];
hddLog(VOS_TRACE_LEVEL_INFO,"acl type = %d no deny mac = %d",
pIe[6], pIe[7]);
if (pConfig->num_deny_mac > MAX_ACL_MAC_ADDRESS)
pConfig->num_deny_mac = MAX_ACL_MAC_ADDRESS;
acl_entry = (struct qc_mac_acl_entry *)(pIe + 8);
for (i = 0; i < pConfig->num_deny_mac; i++)
{
vos_mem_copy(&pConfig->deny_mac[i], acl_entry->addr, sizeof(qcmacaddr));
acl_entry++;
}
}
pIe = wlan_hdd_get_vendor_oui_ie_ptr(WHITELIST_OUI_TYPE, WPA_OUI_TYPE_SIZE,
pBeacon->tail, pBeacon->tail_len);
/* pIe for white list is following form:
type : 1 byte
length : 1 byte
OUI : 4 bytes
acl type : 1 byte
no of mac addr in white list: 1 byte
list of mac_acl_entries: variable, 6 bytes per mac address + sizeof(int) for vlan id
*/
if ((pIe != NULL) && (pIe[1] != 0))
{
pConfig->SapMacaddr_acl = pIe[6];
pConfig->num_accept_mac = pIe[7];
hddLog(VOS_TRACE_LEVEL_INFO,"acl type = %d no accept mac = %d",
pIe[6], pIe[7]);
if (pConfig->num_accept_mac > MAX_ACL_MAC_ADDRESS)
pConfig->num_accept_mac = MAX_ACL_MAC_ADDRESS;
acl_entry = (struct qc_mac_acl_entry *)(pIe + 8);
for (i = 0; i < pConfig->num_accept_mac; i++)
{
vos_mem_copy(&pConfig->accept_mac[i], acl_entry->addr, sizeof(qcmacaddr));
acl_entry++;
}
}
wlan_hdd_set_sapHwmode(pHostapdAdapter);
#ifdef WLAN_FEATURE_11AC
/* Overwrite the hostapd setting for HW mode only for 11ac.
* This is valid only if mode is set to 11n in hostapd, sapEnable11AC
* is set in .ini and 11ac is supported by both host and firmware.
* Otherwise, leave whatever is set in hostapd (a OR b OR g OR n mode)
*/
if( ((pConfig->SapHw_mode == eSAP_DOT11_MODE_11n) ||
(pConfig->SapHw_mode == eSAP_DOT11_MODE_11n_ONLY)) &&
(sapEnable11AC) && (sme_IsFeatureSupportedByDriver(DOT11AC)) &&
(sme_IsFeatureSupportedByFW(DOT11AC)) )
{
v_U32_t operatingBand = 0;
pConfig->SapHw_mode = eSAP_DOT11_MODE_11ac;
ccmCfgGetInt(hHal, WNI_CFG_SAP_CHANNEL_SELECT_OPERATING_BAND, &operatingBand);
/* If ACS disable and selected channel <= 14
* OR
* ACS enabled and ACS operating band is choosen as 2.4
* AND
* VHT in 2.4G Disabled
* THEN
* Fallback to 11N mode
*/
if (((AUTO_CHANNEL_SELECT != pConfig->channel && pConfig->channel <= SIR_11B_CHANNEL_END)
|| (AUTO_CHANNEL_SELECT == pConfig->channel &&
operatingBand == eSAP_RF_SUBBAND_2_4_GHZ)) &&
iniConfig->enableVhtFor24GHzBand == FALSE)
{
hddLog(LOGW, FL("Setting hwmode to 11n, operatingBand = %d, Channel = %d"),
operatingBand, pConfig->channel);
pConfig->SapHw_mode = eSAP_DOT11_MODE_11n;
}
}
#endif
// ht_capab is not what the name conveys,this is used for protection bitmap
pConfig->ht_capab =
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->apProtection;
if ( 0 != wlan_hdd_cfg80211_update_apies(pHostapdAdapter))
{
hddLog(LOGE, FL("SAP Not able to set AP IEs"));
ret = -EINVAL;
goto error;
}
//Uapsd Enabled Bit
pConfig->UapsdEnable =
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->apUapsdEnabled;
//Enable OBSS protection
pConfig->obssProtEnabled =
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->apOBSSProtEnabled;
#ifdef WLAN_FEATURE_11W
pConfig->mfpCapable = MFPCapable;
pConfig->mfpRequired = MFPRequired;
hddLog(LOGW, FL("Soft AP MFP capable %d, MFP required %d\n"),
pConfig->mfpCapable, pConfig->mfpRequired);
#endif
hddLog(LOGW, FL("SOftAP macaddress : "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(pHostapdAdapter->macAddressCurrent.bytes));
hddLog(LOGW,FL("ssid =%s, beaconint=%d, channel=%d"),
pConfig->SSIDinfo.ssid.ssId, (int)pConfig->beacon_int,
(int)pConfig->channel);
hddLog(LOGW,FL("hw_mode=%x, privacy=%d, authType=%d"),
pConfig->SapHw_mode, pConfig->privacy,
pConfig->authType);
hddLog(LOGW,FL("RSN/WPALen=%d, Uapsd = %d"),
(int)pConfig->RSNWPAReqIELength, pConfig->UapsdEnable);
hddLog(LOGW,FL("ProtEnabled = %d, OBSSProtEnabled = %d"),
pConfig->protEnabled, pConfig->obssProtEnabled);
if(test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags))
{
//Bss already started. just return.
//TODO Probably it should update some beacon params.
hddLog( LOGE, "Bss Already started...Ignore the request");
EXIT();
return 0;
}
if (vos_max_concurrent_connections_reached()) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
ret = -EINVAL;
goto error;
}
pConfig->persona = pHostapdAdapter->device_mode;
psmeConfig = (tSmeConfigParams*) vos_mem_malloc(sizeof(tSmeConfigParams));
if ( NULL != psmeConfig)
{
vos_mem_zero(psmeConfig, sizeof (tSmeConfigParams));
sme_GetConfigParam(hHal, psmeConfig);
pConfig->scanBandPreference = psmeConfig->csrConfig.scanBandPreference;
#ifdef WLAN_FEATURE_AP_HT40_24G
if (((pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO))
&& pHddCtx->cfg_ini->apHT40_24GEnabled)
{
psmeConfig->csrConfig.apHT40_24GEnabled = 1;
sme_UpdateConfig (hHal, psmeConfig);
}
#endif
vos_mem_free(psmeConfig);
}
pConfig->acsBandSwitchThreshold = iniConfig->acsBandSwitchThreshold;
set_bit(SOFTAP_INIT_DONE, &pHostapdAdapter->event_flags);
pSapEventCallback = hdd_hostapd_SAPEventCB;
vos_event_reset(&pHostapdState->vosEvent);
if(WLANSAP_StartBss(pVosContext, pSapEventCallback, pConfig,
(v_PVOID_t)pHostapdAdapter->dev) != VOS_STATUS_SUCCESS)
{
hddLog(LOGE,FL("SAP Start Bss fail"));
ret = -EINVAL;
goto error;
}
hddLog(LOG1,
FL("Waiting for Scan to complete(auto mode) and BSS to start"));
status = vos_wait_single_event(&pHostapdState->vosEvent, 10000);
if (!VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: HDD vos wait for single_event failed!!"));
smeGetCommandQStatus(hHal);
VOS_ASSERT(0);
}
set_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags);
if (WLANSAP_get_sessionId(pVosContext, &pHostapdAdapter->sessionId) !=
VOS_STATUS_SUCCESS)
{
hddLog(LOGE,FL("Fail to get Softap sessionID"));
VOS_ASSERT(0);
}
/* Initialize WMM configuation */
hdd_wmm_init(pHostapdAdapter);
wlan_hdd_incr_active_session(pHddCtx, pHostapdAdapter->device_mode);
#ifdef DHCP_SERVER_OFFLOAD
/* set dhcp server offload */
if (iniConfig->enable_dhcp_srv_offload &&
sme_IsFeatureSupportedByFW(SAP_OFFLOADS)) {
vos_event_reset(&pHostapdAdapter->dhcp_status.vos_event);
status = wlan_hdd_set_dhcp_server_offload(pHostapdAdapter, false);
if (!VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("HDD DHCP Server Offload Failed!!"));
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(pHddCtx->pvosContext)) {
status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
ret = -EINVAL;
goto error;
}
}
}
status = vos_wait_single_event(&pHostapdAdapter->dhcp_status.vos_event, 2000);
if (!VOS_IS_STATUS_SUCCESS(status) || pHostapdAdapter->dhcp_status.dhcp_offload_status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: DHCP HDD vos wait for single_event failed!! %d"),
pHostapdAdapter->dhcp_status.dhcp_offload_status);
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(pHddCtx->pvosContext)) {
status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
ret = -EINVAL;
goto error;
}
}
}
#ifdef MDNS_OFFLOAD
if (iniConfig->enable_mdns_offload) {
vos_event_reset(&pHostapdAdapter->mdns_status.vos_event);
status = wlan_hdd_set_mdns_offload(pHostapdAdapter);
if (VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("HDD MDNS Server Offload Failed!!"));
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS ==
WLANSAP_StopBss(pHddCtx->pvosContext)) {
status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
ret = -EINVAL;
goto error;
}
}
}
status = vos_wait_single_event(&pHostapdAdapter->
mdns_status.vos_event, 2000);
if (!VOS_IS_STATUS_SUCCESS(status) ||
pHostapdAdapter->mdns_status.mdns_enable_status ||
pHostapdAdapter->mdns_status.mdns_fqdn_status ||
pHostapdAdapter->mdns_status.mdns_resp_status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("MDNS HDD vos wait for single_event failed!! enable %d fqdn %d resp %d"),
pHostapdAdapter->mdns_status.mdns_enable_status,
pHostapdAdapter->mdns_status.mdns_fqdn_status,
pHostapdAdapter->mdns_status.mdns_resp_status);
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS ==
WLANSAP_StopBss(pHddCtx->pvosContext)) {
status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
ret = -EINVAL;
goto error;
}
}
}
}
#endif /* MDNS_OFFLOAD */
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("DHCP Disabled ini %d, FW %d"),
iniConfig->enable_dhcp_srv_offload,
sme_IsFeatureSupportedByFW(SAP_OFFLOADS));
}
#endif /* DHCP_SERVER_OFFLOAD */
#ifdef WLAN_FEATURE_P2P_DEBUG
if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO)
{
if(globalP2PConnectionStatus == P2P_GO_NEG_COMPLETED)
{
globalP2PConnectionStatus = P2P_GO_COMPLETED_STATE;
hddLog(LOGE,"[P2P State] From Go nego completed to "
"Non-autonomous Group started");
}
else if(globalP2PConnectionStatus == P2P_NOT_ACTIVE)
{
globalP2PConnectionStatus = P2P_GO_COMPLETED_STATE;
hddLog(LOGE,"[P2P State] From Inactive to "
"Autonomous Group started");
}
}
#endif
/* Check and restart SAP if it is on Unsafe channel */
hdd_check_for_unsafe_ch(pHostapdAdapter, pHddCtx);
pHostapdState->bCommit = TRUE;
EXIT();
return 0;
error:
/* Revert the indoor to passive marking if START BSS fails */
if (iniConfig->disable_indoor_channel &&
pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP) {
hdd_update_indoor_channel(pHddCtx, false);
sme_update_channel_list((tpAniSirGlobal)pHddCtx->hHal);
}
clear_bit(SOFTAP_INIT_DONE, &pHostapdAdapter->event_flags);
tdls_enable:
if (ret != eHAL_STATUS_SUCCESS)
wlan_hdd_tdls_reenable(pHddCtx);
return ret;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
static int __wlan_hdd_cfg80211_add_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct beacon_parameters *params)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_BEACON,
pAdapter->sessionId, params->interval));
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "device mode=%s (%d)",
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (vos_max_concurrent_connections_reached()) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
return -EINVAL;
}
if ( (pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
beacon_data_t *old,*new;
old = pAdapter->sessionCtx.ap.beacon;
if (old)
{
hddLog(VOS_TRACE_LEVEL_WARN,
FL("already beacon info added to session(%d)"),
pAdapter->sessionId);
return -EALREADY;
}
status = wlan_hdd_cfg80211_alloc_new_beacon(pAdapter,&new,params);
if(status != VOS_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s:Error!!! Allocating the new beacon",__func__);
return -EINVAL;
}
pAdapter->sessionCtx.ap.beacon = new;
status = wlan_hdd_cfg80211_start_bss(pAdapter, params);
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_add_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct beacon_parameters *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_add_beacon(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_set_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct beacon_parameters *params)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx;
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_BEACON,
pAdapter->sessionId, pHddStaCtx->conn_info.authType));
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
beacon_data_t *old,*new;
old = pAdapter->sessionCtx.ap.beacon;
if (!old)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("session(%d) old and new heads points to NULL"),
pAdapter->sessionId);
return -ENOENT;
}
status = wlan_hdd_cfg80211_alloc_new_beacon(pAdapter,&new,params);
if(status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Error!!! Allocating the new beacon",__func__);
return -EINVAL;
}
pAdapter->sessionCtx.ap.beacon = new;
status = wlan_hdd_cfg80211_start_bss(pAdapter, params);
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_set_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct beacon_parameters *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_beacon(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
#endif //(LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
static int __wlan_hdd_cfg80211_del_beacon(struct wiphy *wiphy,
struct net_device *dev)
#else
static int __wlan_hdd_cfg80211_stop_ap (struct wiphy *wiphy,
struct net_device *dev)
#endif
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_adapter_t *staAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
hdd_scaninfo_t *pScanInfo = NULL;
VOS_STATUS status;
long ret;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter context is Null", __func__);
return -ENODEV;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_STOP_AP,
pAdapter->sessionId, pAdapter->device_mode));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
pScanInfo = &pHddCtx->scan_info;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
/*
* if a sta connection is in progress in another adapter, disconnect
* the sta and complete the sap operation. sta will reconnect
* after sap stop is done.
*/
staAdapter = hdd_get_sta_connection_in_progress(pHddCtx);
if (staAdapter) {
hddLog(LOG1, FL("disconnecting sta with session id: %d"),
staAdapter->sessionId);
wlan_hdd_disconnect(staAdapter, eCSR_DISCONNECT_REASON_DEAUTH);
}
ret = wlan_hdd_scan_abort(pAdapter);
if (ret < 0)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Timeout occurred while waiting for abortscan %ld"), ret);
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: LOGP in Progress. Ignore!!!", __func__);
VOS_ASSERT(pScanInfo->mScanPending);
return -EAGAIN;
}
VOS_ASSERT(pScanInfo->mScanPending);
}
/* Delete all associated STAs before stopping AP/P2P GO */
hdd_del_all_sta(pAdapter);
hdd_hostapd_stop(dev);
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
beacon_data_t *old;
old = pAdapter->sessionCtx.ap.beacon;
if (!old)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("session(%d) beacon data points to NULL"),
pAdapter->sessionId);
return -ENOENT;
}
hdd_cleanup_actionframe(pHddCtx, pAdapter);
mutex_lock(&pHddCtx->sap_lock);
if(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
{
hdd_hostapd_state_t *pHostapdState =
WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
vos_flush_delayed_work(&pHddCtx->ecsa_chan_change_work);
hdd_wait_for_ecsa_complete(pHddCtx);
vos_event_reset(&pHostapdState->vosEvent);
if ( VOS_STATUS_SUCCESS == (status = WLANSAP_StopBss(pHddCtx->pvosContext) ) )
{
status = vos_wait_single_event(&pHostapdState->vosEvent, 10000);
if (!VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: HDD vos wait for single_event failed!!"));
VOS_ASSERT(0);
}
}
clear_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags);
/* BSS stopped, clear the active sessions for this device mode */
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
}
mutex_unlock(&pHddCtx->sap_lock);
if(status != VOS_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s:Error!!! Stopping the BSS",__func__);
return -EINVAL;
}
if (ccmCfgSetInt(pHddCtx->hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, 0,NULL, eANI_BOOLEAN_FALSE)
==eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG to CCM");
}
if ( eHAL_STATUS_FAILURE == ccmCfgSetInt(pHddCtx->hHal,
WNI_CFG_ASSOC_RSP_ADDNIE_FLAG, 0, NULL,
eANI_BOOLEAN_FALSE) )
{
hddLog(LOGE,
"Could not pass on WNI_CFG_ASSOC_RSP_ADDNIE_FLAG to CCM");
}
// Reset WNI_CFG_PROBE_RSP Flags
wlan_hdd_reset_prob_rspies(pAdapter);
clear_bit(SOFTAP_INIT_DONE, &pAdapter->event_flags);
pAdapter->sessionCtx.ap.beacon = NULL;
kfree(old);
#ifdef WLAN_FEATURE_P2P_DEBUG
if((pAdapter->device_mode == WLAN_HDD_P2P_GO) &&
(globalP2PConnectionStatus == P2P_GO_COMPLETED_STATE))
{
hddLog(LOGE,"[P2P State] From GO completed to Inactive state "
"GO got removed");
globalP2PConnectionStatus = P2P_NOT_ACTIVE;
}
#endif
}
EXIT();
return status;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
static int wlan_hdd_cfg80211_del_beacon(struct wiphy *wiphy,
struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_del_beacon(wiphy, dev);
vos_ssr_unprotect(__func__);
return ret;
}
#else
static int wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_stop_ap(wiphy, dev);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,3,0))
static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ap_settings *params)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int status;
ENTER();
if (NULL == dev || NULL == params)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Device or params is Null", __func__);
return -ENODEV;
}
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_START_AP, pAdapter->sessionId,
params-> beacon_interval));
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter magic is invalid", __func__);
return -ENODEV;
}
clear_bit(SOFTAP_INIT_DONE, &pAdapter->event_flags);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: device mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
beacon_data_t *old, *new;
old = pAdapter->sessionCtx.ap.beacon;
if (old)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("already beacon info added to session(%d)"),
pAdapter->sessionId);
return -EALREADY;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
status = wlan_hdd_cfg80211_alloc_new_beacon(pAdapter,
&new,
&params->beacon);
#else
status = wlan_hdd_cfg80211_alloc_new_beacon(pAdapter,
&new,
&params->beacon,
params->dtim_period);
#endif
if (status != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s:Error!!! Allocating the new beacon", __func__);
return -EINVAL;
}
pAdapter->sessionCtx.ap.beacon = new;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
wlan_hdd_cfg80211_set_channel(wiphy, dev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
params->channel, params->channel_type);
#else
params->chandef.chan, cfg80211_get_chandef_type(&(params->chandef)));
#endif
#endif
status = wlan_hdd_cfg80211_start_bss(pAdapter, &params->beacon, params->ssid,
params->ssid_len, params->hidden_ssid,
params->auth_type);
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ap_settings *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_start_ap(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_beacon_data *params)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CHANGE_BEACON,
pAdapter->sessionId, pAdapter->device_mode));
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %d",
__func__, pAdapter->device_mode);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
beacon_data_t *old,*new;
old = pAdapter->sessionCtx.ap.beacon;
if (!old)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("session(%d) beacon data points to NULL"),
pAdapter->sessionId);
return -ENOENT;
}
status = wlan_hdd_cfg80211_alloc_new_beacon(pAdapter, &new, params, 0);
if(status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Error!!! Allocating the new beacon",__func__);
return -EINVAL;
}
pAdapter->sessionCtx.ap.beacon = new;
status = wlan_hdd_cfg80211_start_bss(pAdapter, params, NULL, 0, 0,
pAdapter->sessionCtx.ap.sapConfig.authType);
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_beacon_data *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_change_beacon(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(3,3,0))
static int __wlan_hdd_cfg80211_change_bss (struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
int ret = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CHANGE_BSS,
pAdapter->sessionId, params->ap_isolate));
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
/* ap_isolate == -1 means that in change bss, upper layer doesn't
* want to update this parameter */
if (-1 != params->ap_isolate)
{
pAdapter->sessionCtx.ap.apDisableIntraBssFwd = !!params->ap_isolate;
}
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_change_bss (struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_change_bss(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
/* FUNCTION: wlan_hdd_change_country_code_cd
* to wait for contry code completion
*/
void* wlan_hdd_change_country_code_cb(void *pAdapter)
{
hdd_adapter_t *call_back_pAdapter = pAdapter;
complete(&call_back_pAdapter->change_country_code);
return NULL;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_change_iface
* This function is used to set the interface type (INFRASTRUCTURE/ADHOC)
*/
int __wlan_hdd_cfg80211_change_iface( struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params
)
{
struct wireless_dev *wdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
hdd_context_t *pHddCtx;
tCsrRoamProfile *pRoamProfile = NULL;
hdd_adapter_t *pP2pAdapter = NULL;
eCsrRoamBssType LastBSSType;
hdd_config_t *pConfig = NULL;
eMib_dot11DesiredBssType connectedBssType;
VOS_STATUS status;
long ret;
ENTER();
if (!pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Adapter context is null", __func__);
return VOS_STATUS_E_FAILURE;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is null", __func__);
return VOS_STATUS_E_FAILURE;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CHANGE_IFACE,
pAdapter->sessionId, type));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if (pHddCtx->concurrency_mode == VOS_STA_MON) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: STA + MON is in progress, cannot change interface",
__func__);
}
if (vos_max_concurrent_connections_reached()) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
return -EINVAL;
}
pConfig = pHddCtx->cfg_ini;
wdev = ndev->ieee80211_ptr;
#ifdef WLAN_BTAMP_FEATURE
if((NL80211_IFTYPE_P2P_CLIENT == type)||
(NL80211_IFTYPE_ADHOC == type)||
(NL80211_IFTYPE_AP == type)||
(NL80211_IFTYPE_P2P_GO == type))
{
pHddCtx->isAmpAllowed = VOS_FALSE;
// stop AMP traffic
status = WLANBAP_StopAmp();
if(VOS_STATUS_SUCCESS != status )
{
pHddCtx->isAmpAllowed = VOS_TRUE;
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to stop AMP", __func__);
return -EINVAL;
}
}
#endif //WLAN_BTAMP_FEATURE
/* Reset the current device mode bit mask*/
wlan_hdd_clear_concurrency_mode(pHddCtx, pAdapter->device_mode);
if (((pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) &&
(type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO)) ||
type == NL80211_IFTYPE_AP)
{
/* Notify Mode change in case of concurrency.
* Below function invokes TDLS teardown Functionality Since TDLS is
* not Supported in case of concurrency i.e Once P2P session
* is detected disable offchannel and teardown TDLS links
*/
hddLog(LOG1,
FL("Device mode = %d Interface type = %d"),
pAdapter->device_mode, type);
hdd_tdls_notify_mode_change(pAdapter, pHddCtx);
}
if( (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)
|| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)
)
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
if (!pWextState)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pWextState is null", __func__);
return VOS_STATUS_E_FAILURE;
}
pRoamProfile = &pWextState->roamProfile;
LastBSSType = pRoamProfile->BSSType;
switch (type)
{
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: setting interface Type to INFRASTRUCTURE", __func__);
pRoamProfile->BSSType = eCSR_BSS_TYPE_INFRASTRUCTURE;
#ifdef WLAN_FEATURE_11AC
if(pConfig->dot11Mode == eHDD_DOT11_MODE_AUTO)
{
pConfig->dot11Mode = eHDD_DOT11_MODE_11ac;
}
#endif
pRoamProfile->phyMode =
hdd_cfg_xlate_to_csr_phy_mode(pConfig->dot11Mode);
wdev->iftype = type;
//Check for sub-string p2p to confirm its a p2p interface
if (NULL != strstr(ndev->name,"p2p"))
{
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_exit(pAdapter, TRUE);
mutex_unlock(&pHddCtx->tdls_lock);
#endif
pAdapter->device_mode = (type == NL80211_IFTYPE_STATION) ?
WLAN_HDD_P2P_DEVICE : WLAN_HDD_P2P_CLIENT;
}
else
{
pAdapter->device_mode = (type == NL80211_IFTYPE_STATION) ?
WLAN_HDD_INFRA_STATION: WLAN_HDD_P2P_CLIENT;
}
break;
case NL80211_IFTYPE_ADHOC:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: setting interface Type to ADHOC", __func__);
pRoamProfile->BSSType = eCSR_BSS_TYPE_START_IBSS;
pRoamProfile->phyMode =
hdd_cfg_xlate_to_csr_phy_mode(pConfig->dot11Mode);
pAdapter->device_mode = WLAN_HDD_IBSS;
wdev->iftype = type;
hdd_set_ibss_ops( pAdapter );
hdd_ibss_init_tx_rx( pAdapter );
status = hdd_sta_id_hash_attach(pAdapter);
if (VOS_STATUS_SUCCESS != status) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to initialize hash for IBSS"));
}
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
{
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: setting interface Type to %s", __func__,
(type == NL80211_IFTYPE_AP) ? "SoftAP" : "P2pGo");
//Cancel any remain on channel for GO mode
if (NL80211_IFTYPE_P2P_GO == type)
{
wlan_hdd_cancel_existing_remain_on_channel(pAdapter);
}
if (NL80211_IFTYPE_AP == type)
{
/*
* As Loading WLAN Driver one interface being created
* for p2p device address. This will take one HW STA and
* the max number of clients that can connect to softAP
* will be reduced by one. so while changing the interface
* type to NL80211_IFTYPE_AP (SoftAP) remove p2p0 interface
* as it is not required in SoftAP mode.
*/
// Get P2P Adapter
pP2pAdapter = hdd_get_adapter(pHddCtx,
WLAN_HDD_P2P_DEVICE);
if (pP2pAdapter)
{
wlan_hdd_release_intf_addr(pHddCtx,
pP2pAdapter->macAddressCurrent.bytes);
hdd_stop_adapter(pHddCtx, pP2pAdapter, VOS_TRUE);
hdd_deinit_adapter(pHddCtx, pP2pAdapter, TRUE);
hdd_close_adapter(pHddCtx, pP2pAdapter, VOS_TRUE);
}
}
//Disable IMPS & BMPS for SAP/GO
if(VOS_STATUS_E_FAILURE ==
hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_P2P_GO))
{
//Fail to Exit BMPS
VOS_ASSERT(0);
}
hdd_stop_adapter( pHddCtx, pAdapter, VOS_TRUE );
#ifdef FEATURE_WLAN_TDLS
/* A Mutex Lock is introduced while changing the mode to
* protect the concurrent access for the Adapters by TDLS
* module.
*/
mutex_lock(&pHddCtx->tdls_lock);
#endif
//De-init the adapter.
hdd_deinit_adapter( pHddCtx, pAdapter, TRUE);
memset(&pAdapter->sessionCtx, 0, sizeof(pAdapter->sessionCtx));
pAdapter->device_mode = (type == NL80211_IFTYPE_AP) ?
WLAN_HDD_SOFTAP : WLAN_HDD_P2P_GO;
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) &&
(pConfig->apRandomBssidEnabled))
{
/* 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(pAdapter->macAddressCurrent.bytes, ndev->dev_addr,
VOS_MAC_ADDR_SIZE);
pr_info("wlan: Generated HotSpot BSSID " MAC_ADDRESS_STR"\n",
MAC_ADDR_ARRAY(ndev->dev_addr));
}
hdd_set_ap_ops( pAdapter->dev );
/* This is for only SAP mode where users can
* control country through ini.
* P2P GO follows station country code
* acquired during the STA scanning. */
if((NL80211_IFTYPE_AP == type) &&
(memcmp(pConfig->apCntryCode, CFG_AP_COUNTRY_CODE_DEFAULT, 3) != 0))
{
int status = 0;
VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_INFO,
"%s: setting country code from INI ", __func__);
init_completion(&pAdapter->change_country_code);
status = (int)sme_ChangeCountryCode(pHddCtx->hHal,
(void *)(tSmeChangeCountryCallback)
wlan_hdd_change_country_code_cb,
pConfig->apCntryCode, pAdapter,
pHddCtx->pvosContext,
eSIR_FALSE,
eSIR_TRUE);
if (eHAL_STATUS_SUCCESS == status)
{
/* Wait for completion */
ret = wait_for_completion_interruptible_timeout(
&pAdapter->change_country_code,
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (ret <= 0)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("SME Timed out while setting country code %ld"),
ret);
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: LOGP in Progress. Ignore!!!", __func__);
return -EAGAIN;
}
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: SME Change Country code failed",__func__);
return -EINVAL;
}
}
status = hdd_init_ap_mode(pAdapter, false);
if(status != VOS_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Error initializing the ap mode", __func__);
return -EINVAL;
}
hdd_set_conparam(1);
status = hdd_sta_id_hash_attach(pAdapter);
if (VOS_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to initialize hash for AP"));
return -EINVAL;
}
/*interface type changed update in wiphy structure*/
if(wdev)
{
wdev->iftype = type;
pHddCtx->change_iface = type;
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ERROR !!!! Wireless dev is NULL", __func__);
return -EINVAL;
}
goto done;
}
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Unsupported interface Type",
__func__);
return -EOPNOTSUPP;
}
}
else if ( (pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
switch(type)
{
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
if (pAdapter->device_mode == WLAN_HDD_SOFTAP
&& !hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_DEVICE)) {
/*
* The p2p interface was deleted while SoftAP mode was init,
* create that interface now that the SoftAP is going down.
*/
pP2pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_P2P_DEVICE,
"p2p%d", wlan_hdd_get_intf_addr(pHddCtx),
VOS_TRUE);
}
hdd_stop_adapter( pHddCtx, pAdapter, VOS_TRUE );
#ifdef FEATURE_WLAN_TDLS
/* A Mutex Lock is introduced while changing the mode to
* protect the concurrent access for the Adapters by TDLS
* module.
*/
mutex_lock(&pHddCtx->tdls_lock);
#endif
hdd_deinit_adapter( pHddCtx, pAdapter, TRUE);
wdev->iftype = type;
//Check for sub-string p2p to confirm its a p2p interface
if (NULL != strstr(ndev->name,"p2p"))
{
pAdapter->device_mode = (type == NL80211_IFTYPE_STATION) ?
WLAN_HDD_P2P_DEVICE : WLAN_HDD_P2P_CLIENT;
}
else
{
pAdapter->device_mode = (type == NL80211_IFTYPE_STATION) ?
WLAN_HDD_INFRA_STATION: WLAN_HDD_P2P_CLIENT;
}
/* set con_mode to STA only when no SAP concurrency mode */
if (!(hdd_get_concurrency_mode() & (VOS_SAP | VOS_P2P_GO)))
hdd_set_conparam(0);
pHddCtx->change_iface = type;
memset(&pAdapter->sessionCtx, 0, sizeof(pAdapter->sessionCtx));
hdd_set_station_ops( pAdapter->dev );
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
status = hdd_init_station_mode( pAdapter );
if( VOS_STATUS_SUCCESS != status )
return -EOPNOTSUPP;
/* In case of JB, for P2P-GO, only change interface will be called,
* This is the right place to enable back bmps_imps()
*/
if (pHddCtx->hdd_wlan_suspended)
{
hdd_set_pwrparams(pHddCtx);
}
hdd_enable_bmps_imps(pHddCtx);
goto done;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
wdev->iftype = type;
pAdapter->device_mode = (type == NL80211_IFTYPE_AP) ?
WLAN_HDD_SOFTAP : WLAN_HDD_P2P_GO;
goto done;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Unsupported interface Type",
__func__);
return -EOPNOTSUPP;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: unsupported device mode(%s (%d))",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
return -EOPNOTSUPP;
}
if(pRoamProfile)
{
if ( LastBSSType != pRoamProfile->BSSType )
{
/*interface type changed update in wiphy structure*/
wdev->iftype = type;
/*the BSS mode changed, We need to issue disconnect
if connected or in IBSS disconnect state*/
if ( hdd_connGetConnectedBssType(
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter), &connectedBssType ) ||
( eCSR_BSS_TYPE_START_IBSS == LastBSSType ) )
{
/*need to issue a disconnect to CSR.*/
INIT_COMPLETION(pAdapter->disconnect_comp_var);
if( eHAL_STATUS_SUCCESS ==
sme_RoamDisconnect( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED ) )
{
ret = wait_for_completion_interruptible_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (ret <= 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("wait on disconnect_comp_var failed %ld"), ret);
}
}
}
}
}
done:
/*set bitmask based on updated value*/
wlan_hdd_set_concurrency_mode(pHddCtx, pAdapter->device_mode);
/* Only STA mode support TM now
* all other mode, TM feature should be disabled */
if ( (pHddCtx->cfg_ini->thermalMitigationEnable) &&
(~VOS_STA & pHddCtx->concurrency_mode) )
{
hddDevTmLevelChangedHandler(pHddCtx->parent_dev, 0);
}
#ifdef WLAN_BTAMP_FEATURE
if((NL80211_IFTYPE_STATION == type) && (pHddCtx->concurrency_mode <= 1) &&
(pHddCtx->no_of_open_sessions[WLAN_HDD_INFRA_STATION] <=1))
{
//we are ok to do AMP
pHddCtx->isAmpAllowed = VOS_TRUE;
}
#endif //WLAN_BTAMP_FEATURE
EXIT();
return 0;
}
/*
* FUNCTION: wlan_hdd_cfg80211_change_iface
* wrapper function to protect the actual implementation from SSR.
*/
int wlan_hdd_cfg80211_change_iface( struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params
)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_change_iface(wiphy, ndev, type, flags, params);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_WLAN_TDLS
static int wlan_hdd_tdls_add_station(struct wiphy *wiphy,
struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
bool update, tCsrStaParams *StaParams)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hddTdlsPeer_t *pTdlsPeer;
long ret;
tANI_U16 numCurrTdlsPeers;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
ENTER();
if (!dev) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Dev pointer is NULL"));
return -EINVAL;
}
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adapter is NULL"));
return -EINVAL;
}
if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid arguments");
return -EINVAL;
}
if ((eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode) ||
(eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS mode is disabled OR not enabled in FW."
MAC_ADDRESS_STR " Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
return -ENOTSUPP;
}
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:LOGP in Progress. Ignore!!!", __func__);
wlan_hdd_tdls_set_link_status(pAdapter,
mac,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
return -EBUSY;
}
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, mac);
if ( NULL == pTdlsPeer ) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " (update %d) not exist. return invalid",
__func__, MAC_ADDR_ARRAY(mac), update);
mutex_unlock(&pHddCtx->tdls_lock);
return -EINVAL;
}
/* in add station, we accept existing valid staId if there is */
if ((0 == update) &&
((pTdlsPeer->link_status >= eTDLS_LINK_CONNECTING) ||
(TDLS_STA_INDEX_VALID(pTdlsPeer->staId))))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR
" link_status %d. staId %d. add station ignored.",
__func__, MAC_ADDR_ARRAY(mac), pTdlsPeer->link_status, pTdlsPeer->staId);
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/* in change station, we accept only when staId is valid */
if ((1 == update) &&
((pTdlsPeer->link_status > eTDLS_LINK_CONNECTING) ||
(!TDLS_STA_INDEX_VALID(pTdlsPeer->staId))))
{
tANI_U16 staId = pTdlsPeer->staId;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" link status %d. staId %d. change station %s.",
__func__, MAC_ADDR_ARRAY(mac), pTdlsPeer->link_status, staId,
(TDLS_STA_INDEX_VALID(staId)) ? "ignored" : "declined");
mutex_unlock(&pHddCtx->tdls_lock);
return (TDLS_STA_INDEX_VALID(staId)) ? 0 : -EPERM;
}
mutex_unlock(&pHddCtx->tdls_lock);
/* when others are on-going, we want to change link_status to idle */
if (NULL != wlan_hdd_tdls_is_progress(pHddCtx, mac, TRUE, TRUE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS setup is ongoing. Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
goto error;
}
/* first to check if we reached to maximum supported TDLS peer.
TODO: for now, return -EPERM looks working fine,
but need to check if any other errno fit into this category.*/
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (HDD_MAX_NUM_TDLS_STA <= numCurrTdlsPeers)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected. Request declined."
" Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(mac), numCurrTdlsPeers,
HDD_MAX_NUM_TDLS_STA);
goto error;
}
else
{
hddTdlsPeer_t *pTdlsPeer;
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " already connected. Request declined.",
__func__, MAC_ADDR_ARRAY(mac));
return -EPERM;
}
mutex_unlock(&pHddCtx->tdls_lock);
}
if (0 == update)
wlan_hdd_tdls_set_link_status(pAdapter,
mac,
eTDLS_LINK_CONNECTING,
eTDLS_LINK_SUCCESS);
/* debug code */
if (NULL != StaParams)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS Peer Parameters.", __func__);
if(StaParams->htcap_present)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"ht_capa->cap_info: %0x", StaParams->HTCap.capInfo);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"ht_capa->extended_capabilities: %0x",
StaParams->HTCap.extendedHtCapInfo);
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"params->capability: %0x",StaParams->capability);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"params->ext_capab_len: %0x",StaParams->extn_capability[0]);
if(StaParams->vhtcap_present)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"rxMcsMap %x rxHighest %x txMcsMap %x txHighest %x",
StaParams->VHTCap.suppMcs.rxMcsMap, StaParams->VHTCap.suppMcs.rxHighest,
StaParams->VHTCap.suppMcs.txMcsMap, StaParams->VHTCap.suppMcs.txHighest);
}
{
int i = 0;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "Supported rates:");
for (i = 0; i < sizeof(StaParams->supported_rates); i++)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"[%d]: %x ", i, StaParams->supported_rates[i]);
}
} /* end debug code */
else if ((1 == update) && (NULL == StaParams))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s : update is true, but staParams is NULL. Error!", __func__);
return -EPERM;
}
INIT_COMPLETION(pAdapter->tdls_add_station_comp);
if (!update)
{
/*Before adding sta make sure that device exited from BMPS*/
if (TRUE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Adding tdls peer sta. Disable BMPS", __func__);
status = hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_INFRA_STATION);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set BMPS/IMPS"));
}
}
ret = sme_AddTdlsPeerSta(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, mac);
if (ret != eHAL_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to add TDLS peer STA. Enable Bmps"));
wlan_hdd_tdls_check_bmps(pAdapter);
return -EPERM;
}
}
else
{
ret = sme_ChangeTdlsPeerSta(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, mac, StaParams);
if (ret != eHAL_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to change TDLS peer STA params"));
return -EPERM;
}
}
ret = wait_for_completion_interruptible_timeout(&pAdapter->tdls_add_station_comp,
msecs_to_jiffies(WAIT_TIME_TDLS_ADD_STA));
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if ((pTdlsPeer != NULL) &&
(pTdlsPeer->link_status == eTDLS_LINK_TEARING))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("peer link status %u"), pTdlsPeer->link_status);
mutex_unlock(&pHddCtx->tdls_lock);
goto error;
}
mutex_unlock(&pHddCtx->tdls_lock);
if (ret <= 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: timeout waiting for tdls add station indication %ld",
__func__, ret);
goto error;
}
if ( eHAL_STATUS_SUCCESS != pAdapter->tdlsAddStaStatus)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Add Station is unsucessful", __func__);
return -EPERM;
}
return 0;
error:
wlan_hdd_tdls_set_link_status(pAdapter,
mac,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
return -EPERM;
}
#endif
VOS_STATUS wlan_hdd_send_sta_authorized_event(
hdd_adapter_t *adapter,
hdd_context_t *hdd_ctx,
const v_MACADDR_t *mac_addr)
{
struct sk_buff *vendor_event;
VOS_STATUS status;
struct nl80211_sta_flag_update sta_flags;
ENTER();
if (!hdd_ctx) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is null"));
return -EINVAL;
}
vendor_event =
cfg80211_vendor_event_alloc(
hdd_ctx->wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
&adapter->wdev,
#endif
sizeof(sta_flags) +
VOS_MAC_ADDR_SIZE + NLMSG_HDRLEN,
QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES_INDEX,
GFP_KERNEL);
if (!vendor_event) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("cfg80211_vendor_event_alloc failed"));
return -EINVAL;
}
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) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("STA flag put fails"));
kfree_skb(vendor_event);
return VOS_STATUS_E_FAILURE;
}
status = nla_put(vendor_event,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR,
VOS_MAC_ADDR_SIZE, mac_addr->bytes);
if (status) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("STA MAC put fails"));
kfree_skb(vendor_event);
return VOS_STATUS_E_FAILURE;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
return 0;
}
static int __wlan_hdd_change_station(struct wiphy *wiphy,
struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
hdd_station_ctx_t *pHddStaCtx;
v_MACADDR_t STAMacAddress;
int ret = 0;
#ifdef FEATURE_WLAN_TDLS
tCsrStaParams StaParams = {0};
tANI_U8 isBufSta = 0;
tANI_U8 isOffChannelSupported = 0;
tANI_U8 isQosWmmSta = FALSE;
#endif
ENTER();
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if ((NULL == pAdapter))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid adapter ");
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CHANGE_STATION,
pAdapter->sessionId, params->listen_interval));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL == pHddStaCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid HDD station context");
return -EINVAL;
}
vos_mem_copy(STAMacAddress.bytes, mac, sizeof(v_MACADDR_t));
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO))
{
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
{
status = hdd_softap_change_STA_state( pAdapter, &STAMacAddress,
WLANTL_STA_AUTHENTICATED);
if (status != VOS_STATUS_SUCCESS)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Not able to change TL state to AUTHENTICATED", __func__);
return -EINVAL;
}
status = wlan_hdd_send_sta_authorized_event(pAdapter, pHddCtx,
&STAMacAddress);
if (status != VOS_STATUS_SUCCESS)
return -EINVAL;
}
}
else if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)) {
#ifdef FEATURE_WLAN_TDLS
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
StaParams.capability = params->capability;
StaParams.uapsd_queues = params->uapsd_queues;
StaParams.max_sp = params->max_sp;
/* Convert (first channel , number of channels) tuple to
* the total list of channels. This goes with the assumption
* that if the first channel is < 14, then the next channels
* are an incremental of 1 else an incremental of 4 till the number
* of channels.
*/
if (0 != params->supported_channels_len) {
int i = 0,j = 0,k = 0, no_of_channels = 0 ;
for ( i = 0 ; i < params->supported_channels_len
&& j < SIR_MAC_MAX_SUPP_CHANNELS; i+=2)
{
int wifi_chan_index;
StaParams.supported_channels[j] = params->supported_channels[i];
wifi_chan_index =
((StaParams.supported_channels[j] <= HDD_CHANNEL_14 ) ? 1 : 4 );
no_of_channels = params->supported_channels[i+1];
for(k=1; k <= no_of_channels
&& j < SIR_MAC_MAX_SUPP_CHANNELS - 1; k++)
{
StaParams.supported_channels[j+1] =
StaParams.supported_channels[j] + wifi_chan_index;
j+=1;
}
}
StaParams.supported_channels_len = j;
}
if (params->supported_oper_classes_len >
SIR_MAC_MAX_SUPP_OPER_CLASSES) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"received oper classes:%d, resetting it to max supported %d",
params->supported_oper_classes_len,
SIR_MAC_MAX_SUPP_OPER_CLASSES);
params->supported_oper_classes_len =
SIR_MAC_MAX_SUPP_OPER_CLASSES;
}
vos_mem_copy(StaParams.supported_oper_classes,
params->supported_oper_classes,
params->supported_oper_classes_len);
StaParams.supported_oper_classes_len =
params->supported_oper_classes_len;
if (params->ext_capab_len > sizeof(StaParams.extn_capability)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"received extn capabilities:%d, resetting it to max supported",
params->ext_capab_len);
params->ext_capab_len = sizeof(StaParams.extn_capability);
}
if (0 != params->ext_capab_len)
vos_mem_copy(StaParams.extn_capability, params->ext_capab,
params->ext_capab_len);
if (NULL != params->ht_capa)
{
StaParams.htcap_present = 1;
vos_mem_copy(&StaParams.HTCap, params->ht_capa, sizeof(tSirHTCap));
}
StaParams.supported_rates_len = params->supported_rates_len;
/* Note : The Maximum sizeof supported_rates sent by the Supplicant is 32.
* The supported_rates array , for all the structures propogating till Add Sta
* to the firmware has to be modified , if the supplicant (ieee80211) is
* modified to send more rates.
*/
/* To avoid Data Currption , set to max length to SIR_MAC_MAX_SUPP_RATES
*/
if (StaParams.supported_rates_len > SIR_MAC_MAX_SUPP_RATES)
StaParams.supported_rates_len = SIR_MAC_MAX_SUPP_RATES;
if (0 != StaParams.supported_rates_len) {
int i = 0;
vos_mem_copy(StaParams.supported_rates, params->supported_rates,
StaParams.supported_rates_len);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Supported Rates with Length %d", StaParams.supported_rates_len);
for (i=0; i < StaParams.supported_rates_len; i++)
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"[%d]: %0x", i, StaParams.supported_rates[i]);
}
if (NULL != params->vht_capa)
{
StaParams.vhtcap_present = 1;
vos_mem_copy(&StaParams.VHTCap, params->vht_capa, sizeof(tSirVHTCap));
}
if (0 != params->ext_capab_len ) {
/*Define A Macro : TODO Sunil*/
if ((1<<4) & StaParams.extn_capability[3]) {
isBufSta = 1;
}
/* TDLS Channel Switching Support */
if ((1<<6) & StaParams.extn_capability[3]) {
isOffChannelSupported = 1;
}
}
if (pHddCtx->cfg_ini->fEnableTDLSWmmMode &&
(params->ht_capa || params->vht_capa ||
(params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))))
/* TDLS Peer is WME/QoS capable */
isQosWmmSta = TRUE;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS Peer is QOS capable isQosWmmSta= %d HTcapPresent= %d",
__func__, isQosWmmSta, StaParams.htcap_present);
status = wlan_hdd_tdls_set_peer_caps( pAdapter, mac,
&StaParams, isBufSta,
isOffChannelSupported,
isQosWmmSta);
if (VOS_STATUS_SUCCESS != status) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: wlan_hdd_tdls_set_peer_caps failed!", __func__);
return -EINVAL;
}
status = wlan_hdd_tdls_add_station(wiphy, dev, mac, 1, &StaParams);
if (VOS_STATUS_SUCCESS != status) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_ChangeTdlsPeerSta failed!", __func__);
return -EINVAL;
}
}
#endif
}
EXIT();
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
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 ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_change_station(wiphy, dev, mac, params);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_add_key
* This function is used to initialize the key information
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
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
)
#else
static int __wlan_hdd_cfg80211_add_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, const u8 *mac_addr,
struct key_params *params
)
#endif
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
tCsrRoamSetKey setKey;
u8 groupmacaddr[WNI_CFG_BSSID_LEN] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int status;
v_U32_t roamId= 0xFF;
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
hdd_hostapd_state_t *pHostapdState;
VOS_STATUS vos_status;
eHalStatus halStatus;
hdd_context_t *pHddCtx;
uint8_t i;
v_MACADDR_t *peerMacAddr;
u64 rsc_counter = 0;
uint8_t staid = HDD_MAX_STA_COUNT;
bool pairwise_set_key = false;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_KEY,
pAdapter->sessionId, params->key_len));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if (CSR_MAX_NUM_KEY <= key_index)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid key index %d", __func__,
key_index);
return -EINVAL;
}
if (CSR_MAX_KEY_LEN < params->key_len)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid key length %d", __func__,
params->key_len);
return -EINVAL;
}
if (CSR_MAX_RSC_LEN < params->seq_len)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Invalid seq length %d", __func__,
params->seq_len);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: called with key index = %d & key length %d & seq length %d",
__func__, key_index, params->key_len, params->seq_len);
peerMacAddr = (v_MACADDR_t *)mac_addr;
/*extract key idx, key len and key*/
vos_mem_zero(&setKey,sizeof(tCsrRoamSetKey));
setKey.keyId = key_index;
setKey.keyLength = params->key_len;
vos_mem_copy(&setKey.Key[0],params->key, params->key_len);
vos_mem_copy(&setKey.keyRsc[0], params->seq, params->seq_len);
switch (params->cipher)
{
case WLAN_CIPHER_SUITE_WEP40:
setKey.encType = eCSR_ENCRYPT_TYPE_WEP40_STATICKEY;
break;
case WLAN_CIPHER_SUITE_WEP104:
setKey.encType = eCSR_ENCRYPT_TYPE_WEP104_STATICKEY;
break;
case WLAN_CIPHER_SUITE_TKIP:
{
u8 *pKey = &setKey.Key[0];
setKey.encType = eCSR_ENCRYPT_TYPE_TKIP;
vos_mem_zero(pKey, 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) */
vos_mem_copy(pKey, params->key, 16);
/*Copy the rx mic first*/
vos_mem_copy(&pKey[16], &params->key[24], 8);
/*Copy the tx mic */
vos_mem_copy(&pKey[24], &params->key[16], 8);
break;
}
case WLAN_CIPHER_SUITE_CCMP:
setKey.encType = eCSR_ENCRYPT_TYPE_AES;
break;
#ifdef FEATURE_WLAN_WAPI
case WLAN_CIPHER_SUITE_SMS4:
{
vos_mem_zero(&setKey,sizeof(tCsrRoamSetKey));
wlan_hdd_cfg80211_set_key_wapi(pAdapter, key_index, mac_addr,
params->key, params->key_len);
return 0;
}
#endif
#ifdef FEATURE_WLAN_ESE
case WLAN_CIPHER_SUITE_KRK:
setKey.encType = eCSR_ENCRYPT_TYPE_KRK;
break;
#endif
#ifdef WLAN_FEATURE_11W
case WLAN_CIPHER_SUITE_AES_CMAC:
setKey.encType = eCSR_ENCRYPT_TYPE_AES_CMAC;
break;
#endif
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unsupported cipher type %u",
__func__, params->cipher);
status = -EOPNOTSUPP;
goto end;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: encryption type %d",
__func__, setKey.encType);
if (
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
(!pairwise)
#else
(!mac_addr || is_broadcast_ether_addr(mac_addr))
#endif
)
{
/* set group key*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s- %d: setting Broadcast key",
__func__, __LINE__);
setKey.keyDirection = eSIR_RX_ONLY;
vos_mem_copy(setKey.peerMac,groupmacaddr,WNI_CFG_BSSID_LEN);
}
else
{
/* set pairwise key*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s- %d: setting pairwise key",
__func__, __LINE__);
setKey.keyDirection = eSIR_TX_RX;
vos_mem_copy(setKey.peerMac, mac_addr,WNI_CFG_BSSID_LEN);
pairwise_set_key = true;
}
if ((WLAN_HDD_IBSS == pAdapter->device_mode) && !pairwise)
{
setKey.keyDirection = eSIR_TX_RX;
/*Set the group key*/
status = sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_RoamSetKey failed, returned %d", __func__, status);
status = -EINVAL;
goto end;
}
/*Save the keys here and call sme_RoamSetKey for setting
the PTK after peer joins the IBSS network*/
vos_mem_copy(&pAdapter->sessionCtx.station.ibss_enc_key,
&setKey, sizeof(tCsrRoamSetKey));
goto end;
}
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP) ||
(pAdapter->device_mode == WLAN_HDD_P2P_GO))
{
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
if( pHostapdState->bssState == BSS_START )
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
vos_status = wlan_hdd_check_ula_done(pAdapter);
if (peerMacAddr && (pairwise_set_key == true))
staid = hdd_sta_id_find_from_mac_addr(pAdapter, peerMacAddr);
if ( vos_status != VOS_STATUS_SUCCESS )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"[%4d] wlan_hdd_check_ula_done returned ERROR status= %d",
__LINE__, vos_status );
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
status = -EINVAL;
goto end;
}
status = WLANSAP_SetKeySta( pVosContext, &setKey);
if ( status != eHAL_STATUS_SUCCESS )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"[%4d] WLANSAP_SetKeySta returned ERROR status= %d",
__LINE__, status );
status = -EINVAL;
goto end;
}
}
/* Saving WEP keys */
else if( eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == setKey.encType ||
eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == setKey.encType )
{
//Save the wep key in ap context. Issue setkey after the BSS is started.
hdd_ap_ctx_t *pAPCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
vos_mem_copy(&pAPCtx->wepKey[key_index], &setKey, sizeof(tCsrRoamSetKey));
}
else
{
//Save the key in ap context. Issue setkey after the BSS is started.
hdd_ap_ctx_t *pAPCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
vos_mem_copy(&pAPCtx->groupKey, &setKey, sizeof(tCsrRoamSetKey));
}
}
else if ( (pAdapter->device_mode == WLAN_HDD_INFRA_STATION) ||
(pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) )
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
if (!pairwise)
#else
if (!mac_addr || is_broadcast_ether_addr(mac_addr))
#endif
{
/* set group key*/
if (pHddStaCtx->roam_info.deferKeyComplete)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s- %d: Perform Set key Complete",
__func__, __LINE__);
hdd_PerformRoamSetKeyComplete(pAdapter);
}
}
if (pairwise_set_key == true)
staid = pHddStaCtx->conn_info.staId[0];
pWextState->roamProfile.Keys.KeyLength[key_index] = (u8)params->key_len;
pWextState->roamProfile.Keys.defaultIndex = key_index;
vos_mem_copy(&pWextState->roamProfile.Keys.KeyMaterial[key_index][0],
params->key, params->key_len);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_SETTING_KEY;
hddLog(VOS_TRACE_LEVEL_INFO_MED,
"%s: set key for peerMac %2x:%2x:%2x:%2x:%2x:%2x, direction %d",
__func__, setKey.peerMac[0], setKey.peerMac[1],
setKey.peerMac[2], setKey.peerMac[3],
setKey.peerMac[4], setKey.peerMac[5],
setKey.keyDirection);
vos_status = wlan_hdd_check_ula_done(pAdapter);
if ( vos_status != VOS_STATUS_SUCCESS )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"[%4d] wlan_hdd_check_ula_done returned ERROR status= %d",
__LINE__, vos_status );
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
status = -EINVAL;
goto end;
}
#ifdef WLAN_FEATURE_VOWIFI_11R
/* 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 */
halStatus = sme_FTUpdateKey( WLAN_HDD_GET_HAL_CTX(pAdapter), &setKey);
if ( halStatus == eHAL_STATUS_FT_PREAUTH_KEY_SUCCESS )
{
hddLog(VOS_TRACE_LEVEL_INFO_MED,
"%s: Update PreAuth Key success", __func__);
status = 0;
goto end;
}
else if ( halStatus == eHAL_STATUS_FT_PREAUTH_KEY_FAILED )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Update PreAuth Key failed", __func__);
status = -EINVAL;
goto end;
}
#endif /* WLAN_FEATURE_VOWIFI_11R */
/* issue set key request to SME*/
status = sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_RoamSetKey failed, returned %d", __func__, status);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
status = -EINVAL;
goto end;
}
/* in case of IBSS as there was no information available about WEP keys during
* IBSS join, group key intialized with NULL key, so re-initialize group key
* with correct value*/
if ( (eCSR_BSS_TYPE_START_IBSS == pWextState->roamProfile.BSSType) &&
!( ( IW_AUTH_KEY_MGMT_802_1X
== (pWextState->authKeyMgmt & IW_AUTH_KEY_MGMT_802_1X))
&& (eCSR_AUTH_TYPE_OPEN_SYSTEM == pHddStaCtx->conn_info.authType)
)
&&
( (WLAN_CIPHER_SUITE_WEP40 == params->cipher)
|| (WLAN_CIPHER_SUITE_WEP104 == params->cipher)
)
)
{
setKey.keyDirection = eSIR_RX_ONLY;
vos_mem_copy(setKey.peerMac,groupmacaddr,WNI_CFG_BSSID_LEN);
hddLog(VOS_TRACE_LEVEL_INFO_MED,
"%s: set key peerMac %2x:%2x:%2x:%2x:%2x:%2x, direction %d",
__func__, setKey.peerMac[0], setKey.peerMac[1],
setKey.peerMac[2], setKey.peerMac[3],
setKey.peerMac[4], setKey.peerMac[5],
setKey.keyDirection);
status = sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_RoamSetKey failed for group key (IBSS), returned %d",
__func__, status);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
status = -EINVAL;
goto end;
}
}
}
if (pairwise_set_key == true) {
for (i = 0; i < params->seq_len; i++) {
rsc_counter |= (params->seq[i] << i*8);
}
WLANTL_SetKeySeqCounter(pVosContext, rsc_counter, staid);
}
end:
/* Need to clear any trace of key value in the memory.
* Thus zero out the memory even though it is local
* variable.
*/
vos_mem_zero(&setKey, sizeof(setKey));
EXIT();
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
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
)
#else
static int wlan_hdd_cfg80211_add_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, const u8 *mac_addr,
struct key_params *params
)
#endif
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
ret = __wlan_hdd_cfg80211_add_key(wiphy, ndev, key_index, pairwise,
mac_addr, params);
#else
ret = __wlan_hdd_cfg80211_add_key(wiphy, ndev, key_index, mac_addr,
params);
#endif
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_get_key
* This function is used to get the key information
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
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*)
)
#else
static int __wlan_hdd_cfg80211_get_key(
struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie, struct key_params*)
)
#endif
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
hdd_wext_state_t *pWextState = NULL;
tCsrRoamProfile *pRoamProfile = NULL;
struct key_params params;
hdd_context_t *pHddCtx;
int ret = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
pRoamProfile = &(pWextState->roamProfile);
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
memset(&params, 0, sizeof(params));
if (CSR_MAX_NUM_KEY <= key_index)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("invalid key index %d"), key_index);
return -EINVAL;
}
switch(pRoamProfile->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;
default:
params.cipher = IW_AUTH_CIPHER_NONE;
break;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_KEY,
pAdapter->sessionId, params.cipher));
params.key_len = pRoamProfile->Keys.KeyLength[key_index];
params.seq_len = 0;
params.seq = NULL;
params.key = &pRoamProfile->Keys.KeyMaterial[key_index][0];
callback(cookie, &params);
EXIT();
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
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*)
)
#else
static int wlan_hdd_cfg80211_get_key(
struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, const u8 *mac_addr, void *cookie,
void (*callback)(void *cookie, struct key_params*)
)
#endif
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
ret = __wlan_hdd_cfg80211_get_key(wiphy, ndev, key_index, pairwise,
mac_addr, cookie, callback);
#else
ret = __wlan_hdd_cfg80211_get_key(wiphy, ndev, key_index, mac_addr,
callback);
#endif
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_del_key
* This function is used to delete the key information
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int __wlan_hdd_cfg80211_del_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool pairwise,
const u8 *mac_addr
)
#else
static int __wlan_hdd_cfg80211_del_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
const u8 *mac_addr
)
#endif
{
int status = 0;
//This code needs to be revisited. There is sme_removeKey API, we should
//plan to use that. After the change to use correct index in setkey,
//it is observed that this is invalidating peer
//key index whenever re-key is done. This is affecting data link.
//It should be ok to ignore del_key.
#if 0
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
u8 groupmacaddr[WNI_CFG_BSSID_LEN] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
tCsrRoamSetKey setKey;
v_U32_t roamId= 0xFF;
ENTER();
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: device_mode = %d\n",
__func__,pAdapter->device_mode);
if (CSR_MAX_NUM_KEY <= key_index)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid key index %d", __func__,
key_index);
return -EINVAL;
}
vos_mem_zero(&setKey,sizeof(tCsrRoamSetKey));
setKey.keyId = key_index;
if (mac_addr)
vos_mem_copy(setKey.peerMac, mac_addr,WNI_CFG_BSSID_LEN);
else
vos_mem_copy(setKey.peerMac, groupmacaddr, WNI_CFG_BSSID_LEN);
setKey.encType = eCSR_ENCRYPT_TYPE_NONE;
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
hdd_hostapd_state_t *pHostapdState =
WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
if( pHostapdState->bssState == BSS_START)
{
status = WLANSAP_SetKeySta( pVosContext, &setKey);
if ( status != eHAL_STATUS_SUCCESS )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"[%4d] WLANSAP_SetKeySta returned ERROR status= %d",
__LINE__, status );
}
}
}
else if ( (pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)
)
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_SETTING_KEY;
hddLog(VOS_TRACE_LEVEL_INFO_MED,
"%s: delete key for peerMac %2x:%2x:%2x:%2x:%2x:%2x",
__func__, setKey.peerMac[0], setKey.peerMac[1],
setKey.peerMac[2], setKey.peerMac[3],
setKey.peerMac[4], setKey.peerMac[5]);
if(pAdapter->sessionCtx.station.conn_info.connState ==
eConnectionState_Associated)
{
status = sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_RoamSetKey failure, returned %d",
__func__, status);
pHddStaCtx->roam_info.roamingState = HDD_ROAM_STATE_NONE;
return -EINVAL;
}
}
}
#endif
EXIT();
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int wlan_hdd_cfg80211_del_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool pairwise,
const u8 *mac_addr
)
#else
static int wlan_hdd_cfg80211_del_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
const u8 *mac_addr
)
#endif
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
ret = __wlan_hdd_cfg80211_del_key(wiphy, ndev, key_index, pairwise,
mac_addr);
#else
ret = __wlan_hdd_cfg80211_del_key(wiphy, ndev, key_index, mac_addr);
#endif
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_set_default_key
* This function is used to set the default tx key index
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int __wlan_hdd_cfg80211_set_default_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool unicast, bool multicast)
#else
static int __wlan_hdd_cfg80211_set_default_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index)
#endif
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
int status;
hdd_wext_state_t *pWextState;
hdd_station_ctx_t *pHddStaCtx;
hdd_context_t *pHddCtx;
ENTER();
if ((NULL == pAdapter))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid adapter");
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_DEFAULT_KEY,
pAdapter->sessionId, key_index));
pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if ((NULL == pWextState) || (NULL == pHddStaCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid Wext state or HDD context");
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %d key_index = %d",
__func__,pAdapter->device_mode, key_index);
if (CSR_MAX_NUM_KEY <= key_index)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid key index %d", __func__,
key_index);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)
)
{
if ( (eCSR_ENCRYPT_TYPE_TKIP !=
pHddStaCtx->conn_info.ucEncryptionType) &&
#ifdef FEATURE_WLAN_WAPI
(eCSR_ENCRYPT_TYPE_WPI !=
pHddStaCtx->conn_info.ucEncryptionType) &&
#endif
(eCSR_ENCRYPT_TYPE_AES !=
pHddStaCtx->conn_info.ucEncryptionType)
)
{
/* if default key index is not same as previous one,
* then update the default key index */
tCsrRoamSetKey setKey;
v_U32_t roamId= 0xFF;
tCsrKeys *Keys = &pWextState->roamProfile.Keys;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: default tx key index %d",
__func__, key_index);
Keys->defaultIndex = (u8)key_index;
vos_mem_zero(&setKey,sizeof(tCsrRoamSetKey));
setKey.keyId = key_index;
setKey.keyLength = Keys->KeyLength[key_index];
vos_mem_copy(&setKey.Key[0],
&Keys->KeyMaterial[key_index][0],
Keys->KeyLength[key_index]);
setKey.keyDirection = eSIR_TX_RX;
vos_mem_copy(setKey.peerMac,
&pHddStaCtx->conn_info.bssId[0],
WNI_CFG_BSSID_LEN);
if (Keys->KeyLength[key_index] == CSR_WEP40_KEY_LEN &&
pWextState->roamProfile.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 langht(5) and encryption
type(104) and switching encryption type to 40*/
pWextState->roamProfile.EncryptionType.encryptionType[0] =
eCSR_ENCRYPT_TYPE_WEP40;
pWextState->roamProfile.mcEncryptionType.encryptionType[0] =
eCSR_ENCRYPT_TYPE_WEP40;
}
setKey.encType =
pWextState->roamProfile.EncryptionType.encryptionType[0];
/* issue set key request */
status = sme_RoamSetKey( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &setKey, &roamId );
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_RoamSetKey failed, returned %d", __func__,
status);
return -EINVAL;
}
}
}
/* In SoftAp mode setting key direction for default mode */
else if ( WLAN_HDD_SOFTAP == pAdapter->device_mode )
{
if ( (eCSR_ENCRYPT_TYPE_TKIP !=
pWextState->roamProfile.EncryptionType.encryptionType[0]) &&
(eCSR_ENCRYPT_TYPE_AES !=
pWextState->roamProfile.EncryptionType.encryptionType[0])
)
{
/* Saving key direction for default key index to TX default */
hdd_ap_ctx_t *pAPCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
pAPCtx->wepKey[key_index].keyDirection = eSIR_TX_DEFAULT;
}
}
EXIT();
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int wlan_hdd_cfg80211_set_default_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index,
bool unicast, bool multicast)
#else
static int wlan_hdd_cfg80211_set_default_key( struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index)
#endif
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
ret = __wlan_hdd_cfg80211_set_default_key(wiphy, ndev, key_index, unicast,
multicast);
#else
ret = __wlan_hdd_cfg80211_set_default_key(wiphy, ndev, key_index);
#endif
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: wlan_hdd_cfg80211_inform_bss
* This function is used to inform the BSS details to nl80211 interface.
*/
static struct cfg80211_bss* wlan_hdd_cfg80211_inform_bss(
hdd_adapter_t *pAdapter, tCsrRoamConnectedProfile *roamProfile)
{
struct net_device *dev = pAdapter->dev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
tSirBssDescription *pBssDesc = roamProfile->pBssDesc;
int chan_no;
int ie_length;
const char *ie;
unsigned int freq;
struct ieee80211_channel *chan;
int rssi = 0;
struct cfg80211_bss *bss = NULL;
if( NULL == pBssDesc )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: pBssDesc is NULL", __func__);
return bss;
}
chan_no = pBssDesc->channelId;
ie_length = GET_IE_LEN_IN_BSS_DESC( pBssDesc->length );
ie = ((ie_length != 0) ? (const char *)&pBssDesc->ieFields: NULL);
if( NULL == ie )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: IE of BSS descriptor is NULL", __func__);
return bss;
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38))
if (chan_no <= ARRAY_SIZE(hdd_channels_2_4_GHZ))
{
freq = ieee80211_channel_to_frequency(chan_no, HDD_NL80211_BAND_2GHZ);
}
else
{
freq = ieee80211_channel_to_frequency(chan_no, HDD_NL80211_BAND_5GHZ);
}
#else
freq = ieee80211_channel_to_frequency(chan_no);
#endif
chan = __ieee80211_get_channel(wiphy, freq);
if (!chan) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s chan pointer is NULL", __func__);
return NULL;
}
rssi = (VOS_MIN ((pBssDesc->rssi + pBssDesc->sinr), 0))*100;
return cfg80211_inform_bss(wiphy, chan,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
CFG80211_BSS_FTYPE_UNKNOWN,
#endif
pBssDesc->bssId,
le64_to_cpu(*(__le64 *)pBssDesc->timeStamp),
pBssDesc->capabilityInfo,
pBssDesc->beaconInterval, ie, ie_length,
rssi, GFP_KERNEL );
}
void wlan_hdd_cfg80211_unlink_bss(hdd_adapter_t *pAdapter, tSirMacAddr bssid)
{
struct net_device *dev = pAdapter->dev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_bss *bss = NULL;
bss = hdd_get_bss_entry(wiphy,
NULL, bssid,
NULL, 0);
if (!bss) {
hddLog(LOGE, FL("BSS not present"));
} else {
hddLog(LOG1, FL("cfg80211_unlink_bss called for BSSID "
MAC_ADDRESS_STR), 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);
}
}
/*
* FUNCTION: wlan_hdd_cfg80211_inform_bss_frame
* This function is used to inform the BSS details to nl80211 interface.
*/
struct cfg80211_bss*
wlan_hdd_cfg80211_inform_bss_frame( hdd_adapter_t *pAdapter,
tSirBssDescription *bss_desc
)
{
/*
cfg80211_inform_bss() is not updating ie field of bss entry, if entry
already exists in bss data base of cfg80211 for that particular BSS ID.
Using cfg80211_inform_bss_frame to update the bss entry instead of
cfg80211_inform_bss, But this call expects mgmt packet as input. As of
now there is no possibility to get the mgmt(probe response) frame from PE,
converting bss_desc to ieee80211_mgmt(probe response) and passing to
cfg80211_inform_bss_frame.
*/
struct net_device *dev = pAdapter->dev;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
int chan_no = bss_desc->channelId;
#ifdef WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS
qcom_ie_age *qie_age = NULL;
int ie_length = GET_IE_LEN_IN_BSS_DESC( bss_desc->length ) + sizeof(qcom_ie_age);
#else
int ie_length = GET_IE_LEN_IN_BSS_DESC( bss_desc->length );
#endif
const char *ie =
((ie_length != 0) ? (const char *)&bss_desc->ieFields: NULL);
unsigned int freq;
struct ieee80211_channel *chan;
struct ieee80211_mgmt *mgmt = NULL;
struct cfg80211_bss *bss_status = NULL;
size_t frame_len = ie_length + offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
int rssi = 0;
hdd_context_t *pHddCtx;
int status;
#ifdef WLAN_OPEN_SOURCE
struct timespec ts;
#endif
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return NULL;
}
mgmt = kzalloc(frame_len, GFP_KERNEL);
if (!mgmt)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: memory allocation failed ", __func__);
return NULL;
}
memcpy(mgmt->bssid, bss_desc->bssId, ETH_ALEN);
#ifdef WLAN_OPEN_SOURCE
/* Android does not want the timestamp from the frame.
Instead it wants a monotonic increasing value */
get_monotonic_boottime(&ts);
mgmt->u.probe_resp.timestamp =
((u64)ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
#else
/* keep old behavior for non-open source (for now) */
memcpy(&mgmt->u.probe_resp.timestamp, bss_desc->timeStamp,
sizeof (bss_desc->timeStamp));
#endif
mgmt->u.probe_resp.beacon_int = bss_desc->beaconInterval;
mgmt->u.probe_resp.capab_info = bss_desc->capabilityInfo;
#ifdef WLAN_ENABLE_AGEIE_ON_SCAN_RESULTS
/* 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 = (vos_timer_get_system_time() -
bss_desc->nReceivedTime)/10;
#endif
memcpy(mgmt->u.probe_resp.variable, ie, ie_length);
if (bss_desc->fProbeRsp)
{
mgmt->frame_control |=
(u16)(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
}
else
{
mgmt->frame_control |=
(u16)(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,38))
if (chan_no <= ARRAY_SIZE(hdd_channels_2_4_GHZ) &&
(wiphy->bands[HDD_NL80211_BAND_2GHZ] != NULL))
{
freq = ieee80211_channel_to_frequency(chan_no, HDD_NL80211_BAND_2GHZ);
}
else if ((chan_no > ARRAY_SIZE(hdd_channels_2_4_GHZ)) &&
(wiphy->bands[HDD_NL80211_BAND_5GHZ] != NULL))
{
freq = ieee80211_channel_to_frequency(chan_no, HDD_NL80211_BAND_5GHZ);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s Invalid chan_no:%d",
__func__, chan_no);
kfree(mgmt);
return NULL;
}
#else
freq = ieee80211_channel_to_frequency(chan_no);
#endif
chan = __ieee80211_get_channel(wiphy, freq);
/*when the band is changed on the fly using the GUI, three things are done
* 1. scan abort 2.flush scan results from cache 3.update the band with the new band user specified(refer to the hdd_setBand_helper function)
* as part of the scan abort, message willbe queued to PE and we proceed with flushing and changinh the band.
* pe will stop the scanning further and report back the results what ever it had till now by calling the call back function.
* if the time between update band and scandone call back is sufficent enough the band change reflects in SME, SME validates the channels
* and discards the channels correponding to previous band and calls back with zero bss results.
* but if the time between band update and scan done callback is very small then band change will not reflect in SME and SME reports to HDD
* all the channels correponding to previous band.this is due to race condition.but those channels are invalid to the new band and so
* this function __ieee80211_get_channel will return NULL.Each time we report scan result with this pointer null warning kernel trace is printed.
* if the scan results contain large number of APs continuosly kernel warning trace is printed and it will lead to apps watch dog bark.
* So drop the bss and continue to next bss.
*/
if(chan == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("chan pointer is NULL, chan_no: %d freq: %d"),
chan_no, freq);
kfree(mgmt);
return NULL;
}
/*To keep the rssi icon of the connected AP in the scan window
*and the rssi icon of the wireless networks in sync
* */
if (( eConnectionState_Associated ==
pAdapter->sessionCtx.station.conn_info.connState ) &&
( VOS_TRUE == vos_mem_compare(bss_desc->bssId,
pAdapter->sessionCtx.station.conn_info.bssId,
WNI_CFG_BSSID_LEN)) &&
(pHddCtx->hdd_wlan_suspended == FALSE))
{
/* supplicant takes the signal strength in terms of mBm(100*dBm) */
rssi = (pAdapter->rssi * 100);
}
else
{
rssi = (VOS_MIN ((bss_desc->rssi + bss_desc->sinr), 0))*100;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: BSSID:" MAC_ADDRESS_STR " Channel:%d"
" RSSI:%d", __func__, MAC_ADDR_ARRAY(mgmt->bssid),
vos_freq_to_chan(chan->center_freq), (int)(rssi/100));
bss_status = cfg80211_inform_bss_frame(wiphy, chan, mgmt,
frame_len, rssi, GFP_KERNEL);
kfree(mgmt);
return bss_status;
}
/*
* FUNCTION: wlan_hdd_cfg80211_update_bss_db
* This function is used to update the BSS data base of CFG8011
*/
struct cfg80211_bss* wlan_hdd_cfg80211_update_bss_db( hdd_adapter_t *pAdapter,
tCsrRoamInfo *pRoamInfo
)
{
tCsrRoamConnectedProfile roamProfile;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
struct cfg80211_bss *bss = NULL;
ENTER();
memset(&roamProfile, 0, sizeof(tCsrRoamConnectedProfile));
sme_RoamGetConnectProfile(hHal, pAdapter->sessionId, &roamProfile);
if (NULL != roamProfile.pBssDesc)
{
bss = wlan_hdd_cfg80211_inform_bss_frame(pAdapter,
roamProfile.pBssDesc);
if (NULL == bss)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: cfg80211_inform_bss return NULL",
__func__);
}
sme_RoamFreeConnectProfile(hHal, &roamProfile);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: roamProfile.pBssDesc is NULL",
__func__);
}
return bss;
}
/*
* FUNCTION: wlan_hdd_cfg80211_update_bss
*/
static int wlan_hdd_cfg80211_update_bss( struct wiphy *wiphy,
hdd_adapter_t *pAdapter
)
{
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
tCsrScanResultInfo *pScanResult;
eHalStatus status = 0;
int ret;
tScanResultHandle pResult;
struct cfg80211_bss *bss_status = NULL;
hdd_context_t *pHddCtx;
bool is_p2p_scan = false;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_UPDATE_BSS,
NO_SESSION, pAdapter->sessionId));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
if (pAdapter->request != NULL)
{
if ((pAdapter->request->n_ssids == 1)
&& (pAdapter->request->ssids != NULL)
&& vos_mem_compare(&pAdapter->request->ssids[0], "DIRECT-", 7))
is_p2p_scan = true;
}
/*
* start getting scan results and populate cgf80211 BSS database
*/
status = sme_ScanGetResult(hHal, pAdapter->sessionId, NULL, &pResult);
/* no scan results */
if (NULL == pResult)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: No scan result Status %d",
__func__, status);
wlan_hdd_get_frame_logs(pAdapter,
WLAN_HDD_GET_FRAME_LOG_CMD_SEND_AND_CLEAR);
return status;
}
pScanResult = sme_ScanResultGetFirst(hHal, pResult);
while (pScanResult)
{
/*
* cfg80211_inform_bss() is not updating ie field of bss entry, if
* entry already exists in bss data base of cfg80211 for that
* particular BSS ID. Using cfg80211_inform_bss_frame to update the
* bss entry instead of cfg80211_inform_bss, But this call expects
* mgmt packet as input. As of now there is no possibility to get
* the mgmt(probe response) frame from PE, converting bss_desc to
* ieee80211_mgmt(probe response) and passing to c
* fg80211_inform_bss_frame.
* */
if(is_p2p_scan && (pScanResult->ssId.ssId != NULL) &&
!vos_mem_compare( pScanResult->ssId.ssId, "DIRECT-", 7) )
{
pScanResult = sme_ScanResultGetNext(hHal, pResult);
continue; //Skip the non p2p bss entries
}
bss_status = wlan_hdd_cfg80211_inform_bss_frame(pAdapter,
&pScanResult->BssDescriptor);
if (NULL == bss_status)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: NULL returned by cfg80211_inform_bss", __func__);
}
else
{
cfg80211_put_bss(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0))
wiphy,
#endif
bss_status);
}
pScanResult = sme_ScanResultGetNext(hHal, pResult);
}
sme_ScanResultPurge(hHal, pResult);
is_p2p_scan = false;
return 0;
}
void
hddPrintMacAddr(tCsrBssid macAddr, tANI_U8 logLevel)
{
VOS_TRACE(VOS_MODULE_ID_HDD, logLevel,
MAC_ADDRESS_STR, MAC_ADDR_ARRAY(macAddr));
} /****** end hddPrintMacAddr() ******/
void
hddPrintPmkId(tANI_U8 *pmkId, tANI_U8 logLevel)
{
VOS_TRACE(VOS_MODULE_ID_HDD, logLevel,
"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
pmkId[0], pmkId[1], pmkId[2], pmkId[3], pmkId[4],
pmkId[5], pmkId[6], pmkId[7], pmkId[8], pmkId[9], pmkId[10],
pmkId[11], pmkId[12], pmkId[13], pmkId[14], pmkId[15]);
} /****** end hddPrintPmkId() ******/
//hddPrintMacAddr(tCsrBssid macAddr, tANI_U8 logLevel);
//hddPrintMacAddr(macAddr, VOS_TRACE_LEVEL_FATAL);
//void sirDumpBuf(tpAniSirGlobal pMac, tANI_U8 modId, tANI_U32 level, tANI_U8 *buf, tANI_U32 size);
//sirDumpBuf(pMac, VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, pmkid, 16);
#define dump_bssid(bssid) \
{ \
hddLog(VOS_TRACE_LEVEL_INFO, "BSSID (MAC) address:\t"); \
hddPrintMacAddr(bssid, VOS_TRACE_LEVEL_INFO);\
}
#define dump_pmkid(pMac, pmkid) \
{ \
hddLog(VOS_TRACE_LEVEL_INFO, "PMKSA-ID:\t"); \
hddPrintPmkId(pmkid, VOS_TRACE_LEVEL_INFO);\
}
#if defined(FEATURE_WLAN_LFR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
/*
* FUNCTION: wlan_hdd_cfg80211_pmksa_candidate_notify
* This function is used to notify the supplicant of a new PMKSA candidate.
*/
int wlan_hdd_cfg80211_pmksa_candidate_notify(
hdd_adapter_t *pAdapter, tCsrRoamInfo *pRoamInfo,
int index, bool preauth )
{
#ifdef FEATURE_WLAN_OKC
struct net_device *dev = pAdapter->dev;
hdd_context_t *pHddCtx = (hdd_context_t*)pAdapter->pHddCtx;
ENTER();
hddLog(VOS_TRACE_LEVEL_INFO, "%s is going to notify supplicant of:", __func__);
if( NULL == pRoamInfo )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: pRoamInfo is NULL", __func__);
return -EINVAL;
}
if (eANI_BOOLEAN_TRUE == hdd_is_okc_mode_enabled(pHddCtx))
{
dump_bssid(pRoamInfo->bssid);
cfg80211_pmksa_candidate_notify(dev, index,
pRoamInfo->bssid, preauth, GFP_KERNEL);
}
#endif /* FEATURE_WLAN_OKC */
return 0;
}
#endif //FEATURE_WLAN_LFR
#ifdef FEATURE_WLAN_LFR_METRICS
/*
* FUNCTION: wlan_hdd_cfg80211_roam_metrics_preauth
* 802.11r/LFR metrics reporting function to report preauth initiation
*
*/
#define MAX_LFR_METRICS_EVENT_LENGTH 100
VOS_STATUS wlan_hdd_cfg80211_roam_metrics_preauth(hdd_adapter_t *pAdapter,
tCsrRoamInfo *pRoamInfo)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
ENTER();
if (NULL == pAdapter)
{
hddLog(LOGE, "%s: pAdapter is NULL!", __func__);
return VOS_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 "
MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pRoamInfo->bssid));
wireless_send_event(pAdapter->dev, IWEVCUSTOM, &wrqu, metrics_notification);
EXIT();
return VOS_STATUS_SUCCESS;
}
/*
* FUNCTION: wlan_hdd_cfg80211_roam_metrics_preauth_status
* 802.11r/LFR metrics reporting function to report preauth completion
* or failure
*/
VOS_STATUS wlan_hdd_cfg80211_roam_metrics_preauth_status(
hdd_adapter_t *pAdapter, tCsrRoamInfo *pRoamInfo, bool preauth_status)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
ENTER();
if (NULL == pAdapter)
{
hddLog(LOGE, "%s: pAdapter is NULL!", __func__);
return VOS_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 "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pRoamInfo->bssid));
if (1 == preauth_status)
strncat(metrics_notification, " TRUE", 5);
else
strncat(metrics_notification, " FALSE", 6);
wrqu.data.pointer = metrics_notification;
wrqu.data.length = strlen(metrics_notification);
wireless_send_event(pAdapter->dev, IWEVCUSTOM, &wrqu, metrics_notification);
EXIT();
return VOS_STATUS_SUCCESS;
}
/*
* FUNCTION: wlan_hdd_cfg80211_roam_metrics_handover
* 802.11r/LFR metrics reporting function to report handover initiation
*
*/
VOS_STATUS wlan_hdd_cfg80211_roam_metrics_handover(hdd_adapter_t * pAdapter,
tCsrRoamInfo *pRoamInfo)
{
unsigned char metrics_notification[MAX_LFR_METRICS_EVENT_LENGTH + 1];
union iwreq_data wrqu;
ENTER();
if (NULL == pAdapter)
{
hddLog(LOGE, "%s: pAdapter is NULL!", __func__);
return VOS_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 "
MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pRoamInfo->bssid));
wireless_send_event(pAdapter->dev, IWEVCUSTOM, &wrqu, metrics_notification);
EXIT();
return VOS_STATUS_SUCCESS;
}
#endif
/**
* wlan_hdd_cfg80211_validate_scan_req - validate scan request
* @scan_req: scan request to be checked
*
* Return: true or false
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
static inline bool wlan_hdd_cfg80211_validate_scan_req(struct
cfg80211_scan_request
*scan_req, hdd_context_t
*hdd_ctx)
{
if (!scan_req || !scan_req->wiphy ||
scan_req->wiphy != hdd_ctx->wiphy) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Invalid scan request");
return false;
}
if (vos_is_load_unload_in_progress(VOS_MODULE_ID_HDD, NULL)) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Load/Unload in progress");
return false;
}
return true;
}
#else
static inline bool wlan_hdd_cfg80211_validate_scan_req(struct
cfg80211_scan_request
*scan_req, hdd_context_t
*hdd_ctx)
{
if (!scan_req || !scan_req->wiphy ||
scan_req->wiphy != hdd_ctx->wiphy) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Invalid scan request");
return false;
}
return true;
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
/**
* hdd_cfg80211_scan_done() - Scan completed callback to cfg80211
* @adapter: Pointer to the adapter
* @req : Scan request
* @aborted : true scan aborted false scan success
*
* This function notifies scan done to cfg80211
*
* Return: none
*/
static void hdd_cfg80211_scan_done(hdd_adapter_t *adapter,
struct cfg80211_scan_request *req,
bool aborted)
{
struct cfg80211_scan_info info = {
.aborted = aborted
};
if (adapter->dev->flags & IFF_UP)
cfg80211_scan_done(req, &info);
else
hddLog(LOGW,
FL("IFF_UP flag reset for %s"), adapter->dev->name);
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
/**
* hdd_cfg80211_scan_done() - Scan completed callback to cfg80211
* @adapter: Pointer to the adapter
* @req : Scan request
* @aborted : true scan aborted false scan success
*
* This function notifies scan done to cfg80211
*
* Return: none
*/
static void hdd_cfg80211_scan_done(hdd_adapter_t *adapter,
struct cfg80211_scan_request *req,
bool aborted)
{
if (adapter->dev->flags & IFF_UP)
cfg80211_scan_done(req, aborted);
else
hddLog(LOGW,
FL("IFF_UP flag reset for %s"), adapter->dev->name);
}
#else
/**
* hdd_cfg80211_scan_done() - Scan completed callback to cfg80211
* @adapter: Pointer to the adapter
* @req : Scan request
* @aborted : true scan aborted false scan success
*
* This function notifies scan done to cfg80211
*
* Return: none
*/
static void hdd_cfg80211_scan_done(hdd_adapter_t *adapter,
struct cfg80211_scan_request *req,
bool aborted)
{
cfg80211_scan_done(req, aborted);
}
#endif
#define NET_DEV_IS_IFF_UP(pAdapter) (pAdapter->dev->flags & IFF_UP)
/*
* FUNCTION: hdd_cfg80211_scan_done_callback
* scanning callback function, called after finishing scan
*
*/
static eHalStatus hdd_cfg80211_scan_done_callback(tHalHandle halHandle,
void *pContext, tANI_U32 scanId, eCsrScanStatus status)
{
struct net_device *dev = (struct net_device *) pContext;
//struct wireless_dev *wdev = dev->ieee80211_ptr;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_scaninfo_t *pScanInfo;
struct cfg80211_scan_request *req = NULL;
int ret = 0;
bool aborted = false;
long waitRet = 0;
hdd_context_t *pHddCtx;
ENTER();
if (!pAdapter || pAdapter->magic != WLAN_HDD_ADAPTER_MAGIC ||
!pAdapter->dev) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Adapter is not valid"));
return 0;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is Null"));
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
if (!NET_DEV_IS_IFF_UP(pAdapter))
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Interface is down"));
}
#endif
pScanInfo = &pHddCtx->scan_info;
hddLog(VOS_TRACE_LEVEL_INFO,
"%s called with halHandle = %pK, pContext = %pK,"
"scanID = %d, returned status = %d",
__func__, halHandle, pContext, (int) scanId, (int) status);
pScanInfo->mScanPendingCounter = 0;
//Block on scan req completion variable. Can't wait forever though.
waitRet = wait_for_completion_interruptible_timeout(
&pScanInfo->scan_req_completion_event,
msecs_to_jiffies(WLAN_WAIT_TIME_SCAN_REQ));
if (waitRet <= 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s wait on scan_req_completion_event failed %ld",__func__, waitRet);
VOS_ASSERT(pScanInfo->mScanPending);
goto allow_suspend;
}
if (pScanInfo->mScanPending != VOS_TRUE)
{
VOS_ASSERT(pScanInfo->mScanPending);
goto allow_suspend;
}
/* Check the scanId */
if (pScanInfo->scanId != scanId)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s called with mismatched scanId pScanInfo->scanId = %d "
"scanId = %d", __func__, (int) pScanInfo->scanId,
(int) scanId);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
if (NET_DEV_IS_IFF_UP(pAdapter))
#endif
{
ret = wlan_hdd_cfg80211_update_bss((WLAN_HDD_GET_CTX(pAdapter))->wiphy,
pAdapter);
if (0 > ret)
hddLog(VOS_TRACE_LEVEL_INFO, "%s: NO SCAN result", __func__);
}
/* If any client wait scan result through WEXT
* send scan done event to client */
if (pHddCtx->scan_info.waitScanResult)
{
/* The other scan request waiting for current scan finish
* Send event to notify current scan finished */
if(WEXT_SCAN_PENDING_DELAY == pHddCtx->scan_info.scan_pending_option)
{
vos_event_set(&pHddCtx->scan_info.scan_finished_event);
}
/* Send notify to WEXT client */
else if(WEXT_SCAN_PENDING_PIGGYBACK == pHddCtx->scan_info.scan_pending_option)
{
struct net_device *dev = pAdapter->dev;
union iwreq_data wrqu;
int we_event;
char *msg;
memset(&wrqu, '\0', sizeof(wrqu));
we_event = SIOCGIWSCAN;
msg = NULL;
wireless_send_event(dev, we_event, &wrqu, msg);
}
}
pHddCtx->scan_info.waitScanResult = FALSE;
/* Get the Scan Req */
req = pAdapter->request;
pAdapter->request = NULL;
/* Scan is no longer pending */
pScanInfo->mScanPending = VOS_FALSE;
if (!wlan_hdd_cfg80211_validate_scan_req(req, pHddCtx))
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
hddLog(VOS_TRACE_LEVEL_ERROR, FL("interface state %s"),
NET_DEV_IS_IFF_UP(pAdapter) ? "up" : "down");
#endif
if (pAdapter->dev) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("device name %s"),
pAdapter->dev->name);
}
complete(&pScanInfo->abortscan_event_var);
goto allow_suspend;
}
/* last_scan_timestamp is used to decide if new scan
* is needed or not on station interface. If last station
* scan time and new station scan time is less then
* last_scan_timestamp ; driver will return cached scan.
* Also only last_scan_timestamp is updated here last_scan_channellist
* is updated on receiving scan request itself to make sure kernel
* allocated scan request(scan_req) object is not dereferenced here,
* because interface down, where kernel frees scan_req, may happen any
* time while driver is processing scan_done_callback. So it's better
* not to access scan_req in this routine.
*/
if (pScanInfo->no_cck == FALSE) { // no_cck will be set during p2p find
if (status == eCSR_SCAN_SUCCESS)
pScanInfo->last_scan_timestamp = vos_timer_get_system_time();
else {
vos_mem_zero(pHddCtx->scan_info.last_scan_channelList,
sizeof(pHddCtx->scan_info.last_scan_channelList));
pHddCtx->scan_info.last_scan_numChannels = 0;
pScanInfo->last_scan_timestamp = 0;
}
}
/*
* cfg80211_scan_done informing NL80211 about completion
* of scanning
*/
if (status == eCSR_SCAN_ABORT || status == eCSR_SCAN_FAILURE)
{
aborted = true;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
if (NET_DEV_IS_IFF_UP(pAdapter) &&
wlan_hdd_cfg80211_validate_scan_req(req, pHddCtx))
#endif
hdd_cfg80211_scan_done(pAdapter, req, aborted);
complete(&pScanInfo->abortscan_event_var);
allow_suspend:
if ((pHddCtx->cfg_ini->enableMacSpoofing == MAC_ADDR_SPOOFING_FW_HOST_ENABLE
) && (pHddCtx->spoofMacAddr.isEnabled
|| pHddCtx->spoofMacAddr.isReqDeferred)) {
/* Generate new random mac addr for next scan */
hddLog(VOS_TRACE_LEVEL_INFO, "scan completed - generate new spoof mac addr");
schedule_delayed_work(&pHddCtx->spoof_mac_addr_work,
msecs_to_jiffies(MAC_ADDR_SPOOFING_DEFER_INTERVAL));
}
/* release the wake lock at the end of the scan*/
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_SCAN);
/* Acquire wakelock to handle the case where APP's tries to suspend
* immediatly after the driver gets connect request(i.e after scan)
* from supplicant, this result in app's is suspending and not able
* to process the connect request to AP */
hdd_prevent_suspend_timeout(1000, WIFI_POWER_EVENT_WAKELOCK_SCAN);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
if (NET_DEV_IS_IFF_UP(pAdapter))
#endif
#ifdef FEATURE_WLAN_TDLS
wlan_hdd_tdls_scan_done_callback(pAdapter);
#endif
EXIT();
return 0;
}
/*
* FUNCTION: hdd_isConnectionInProgress
* Go through each adapter and check if Connection is in progress
*
*/
v_BOOL_t hdd_isConnectionInProgress(hdd_context_t *pHddCtx, v_U8_t *session_id,
scan_reject_states *reason)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_station_ctx_t *pHddStaCtx = NULL;
hdd_adapter_t *pAdapter = NULL;
VOS_STATUS status = 0;
v_U8_t staId = 0;
v_U8_t *staMac = NULL;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( pAdapter )
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Adapter with device mode %s (%d) exists",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if (((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)) &&
(eConnectionState_Connecting ==
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState))
{
hddLog(LOG1,
"%s: %pK(%d) Connection is in progress", __func__,
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter), pAdapter->sessionId);
if (session_id && reason)
{
*session_id = pAdapter->sessionId;
*reason = eHDD_CONNECTION_IN_PROGRESS;
}
return VOS_TRUE;
}
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
smeNeighborMiddleOfRoaming(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
hddLog(LOG1,
"%s: %pK(%d) Reassociation is in progress", __func__,
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter), pAdapter->sessionId);
if (session_id && reason)
{
*session_id = pAdapter->sessionId;
*reason = eHDD_REASSOC_IN_PROGRESS;
}
return VOS_TRUE;
}
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode))
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
sme_is_sta_key_exchange_in_progress(pHddCtx->hHal,
pAdapter->sessionId))
{
staMac = (v_U8_t *) &(pAdapter->macAddressCurrent.bytes[0]);
hddLog(LOG1,
"%s: client " MAC_ADDRESS_STR
" is in the middle of WPS/EAPOL exchange.", __func__,
MAC_ADDR_ARRAY(staMac));
if (session_id && reason)
{
*session_id = pAdapter->sessionId;
*reason = eHDD_EAPOL_IN_PROGRESS;
}
return VOS_TRUE;
}
}
else if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
(WLAN_HDD_P2P_GO == pAdapter->device_mode))
{
v_CONTEXT_t pVosContext = ( WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
ptSapContext pSapCtx = NULL;
pSapCtx = VOS_GET_SAP_CB(pVosContext);
if(pSapCtx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("psapCtx is NULL"));
return VOS_FALSE;
}
for (staId = 0; staId < WLAN_MAX_STA_COUNT; staId++)
{
if ((pSapCtx->aStaInfo[staId].isUsed) &&
(WLANTL_STA_CONNECTED == pSapCtx->aStaInfo[staId].tlSTAState))
{
staMac = (v_U8_t *) &(pSapCtx->aStaInfo[staId].macAddrSTA.bytes[0]);
hddLog(LOG1,
"%s: client " MAC_ADDRESS_STR " of SoftAP/P2P-GO is in the "
"middle of WPS/EAPOL exchange.", __func__,
MAC_ADDR_ARRAY(staMac));
if (session_id && reason)
{
*session_id = pAdapter->sessionId;
*reason = eHDD_SAP_EAPOL_IN_PROGRESS;
}
return VOS_TRUE;
}
}
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return VOS_FALSE;
}
/**
* csr_scan_request_assign_bssid() - Set the BSSID received from Supplicant
* to the Scan request
* @scanRequest: Pointer to the csr scan request
* @request: Pointer to the scan request from supplicant
*
* Return: None
*/
#ifdef CFG80211_SCAN_BSSID
static inline void csr_scan_request_assign_bssid(tCsrScanRequest *scanRequest,
struct cfg80211_scan_request *request)
{
vos_mem_copy(scanRequest->bssid, request->bssid, VOS_MAC_ADDR_SIZE);
}
#else
static inline void csr_scan_request_assign_bssid(tCsrScanRequest *scanRequest,
struct cfg80211_scan_request *request)
{
}
#endif
#if defined(CFG80211_SCAN_RANDOM_MAC_ADDR) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
/**
* hdd_is_wiphy_scan_random_support() - Check NL80211 scan randomization support
* @wiphy: Pointer to wiphy structure
*
* This function is used to check whether @wiphy supports
* NL80211 scan randomization feature.
*
* Return: If randomization is supported then return true else false.
*/
static bool
hdd_is_wiphy_scan_random_support(struct wiphy *wiphy)
{
if (wiphy->features & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
return true;
return false;
}
/**
* hdd_is_nl_scan_random() - Check for randomization flag in cfg80211 scan
* @nl_scan: cfg80211 scan request
*
* This function is used to check whether scan randomization flag is set for
* current cfg80211 scan request identified by @nl_scan.
*
* Return: If randomization flag is set then return true else false.
*/
static bool
hdd_is_nl_scan_random(struct cfg80211_scan_request *nl_scan)
{
if (nl_scan->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
return true;
return false;
}
#else
static bool
hdd_is_wiphy_scan_random_support(struct wiphy *wiphy)
{
return false;
}
static bool
hdd_is_nl_scan_random(struct cfg80211_scan_request *nl_scan)
{
return false;
}
#endif
/**
* hdd_generate_scan_random_mac() - Generate Random mac addr for cfg80211 scan
* @mac_addr: Input mac-addr from which random-mac address is to be generated
* @mac_mask: Bits of mac_addr which should not be randomized
* @random_mac: Output pointer to hold generated random mac address
*
* This function is used generate random mac address using @mac_addr and
* @mac_mask with following logic:
* Bit value 0 in the mask means that we should randomize that bit.
* Bit value 1 in the mask means that we should take specific bit value
* from mac address provided.
*
* Return: None
*/
static void
hdd_generate_scan_random_mac(uint8_t *mac_addr, uint8_t *mac_mask,
uint8_t *random_mac)
{
uint32_t i;
uint8_t random_byte;
for (i = 0; i < VOS_MAC_ADDRESS_LEN; i++) {
random_byte = 0;
get_random_bytes(&random_byte, 1);
random_mac[i] = (mac_addr[i] & mac_mask[i]) |
(random_byte & (~(mac_mask[i])));
}
/*
* Make sure locally administered bit is set if that
* particular bit in the mask is 0
*/
if (!(mac_mask[0] & 0x2))
random_mac[0] |= 0x2;
/*
* Make sure multicast/group address bit is NOT set if that
* particular bit in the mask is 0
*/
if (!(mac_mask[0] & 0x1))
random_mac[0] &= ~0x1;
}
/**
* hdd_spoof_scan() - Spoof cfg80211 scan
* @wiphy: Pointer to wiphy
* @adapter: Pointer to adapter for which scan is requested
* @nl_scan: Cfg80211 scan request
* @is_p2p_scan: Check for p2p scan
* @csr_scan: Pointer to internal (csr) scan request
*
* This function is used for following purposes:
* (a) If cfg80211 supports scan randomization then this function invokes helper
* functions to generate random-mac address.
* (b) If the cfg80211 doesn't support scan randomization then randomize scans
* using spoof mac received with VENDOR_SUBCMD_MAC_OUI.
* (c) Configure the random-mac in transport layer.
*
* Return: For success return 0 else return negative value.
*/
static int
hdd_spoof_scan(struct wiphy *wiphy, hdd_adapter_t *adapter,
struct cfg80211_scan_request *nl_scan,
bool is_p2p_scan, tCsrScanRequest *csr_scan)
{
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
hdd_config_t *config = hdd_ctx->cfg_ini;
uint8_t random_mac[VOS_MAC_ADDRESS_LEN];
VOS_STATUS vos_status;
eHalStatus hal_status;
csr_scan->nl_scan = true;
csr_scan->scan_randomize = false;
if (config->enableMacSpoofing != MAC_ADDR_SPOOFING_FW_HOST_ENABLE ||
!sme_IsFeatureSupportedByFW(MAC_SPOOFED_SCAN))
return 0;
vos_flush_delayed_work(&hdd_ctx->spoof_mac_addr_work);
if (hdd_is_wiphy_scan_random_support(wiphy)) {
if (!hdd_is_nl_scan_random(nl_scan) || is_p2p_scan)
return 0;
hdd_generate_scan_random_mac(nl_scan->mac_addr,
nl_scan->mac_addr_mask,
random_mac);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("cfg80211 scan random attributes:"));
hddLog(VOS_TRACE_LEVEL_INFO, "mac-addr: "MAC_ADDRESS_STR
" mac-mask: "MAC_ADDRESS_STR
" random-mac: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(nl_scan->mac_addr),
MAC_ADDR_ARRAY(nl_scan->mac_addr_mask),
MAC_ADDR_ARRAY(random_mac));
hal_status = sme_SpoofMacAddrReq(hdd_ctx->hHal,
(v_MACADDR_t *)random_mac,
false);
if (hal_status != eHAL_STATUS_SUCCESS) {
hddLog(LOGE,
FL("Send of Spoof request failed"));
hddLog(LOGE,
FL("Disable spoofing and use self-mac"));
return 0;
}
vos_status = WLANTL_updateSpoofMacAddr(hdd_ctx->pvosContext,
(v_MACADDR_t*)random_mac,
&adapter->macAddressCurrent);
if(vos_status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to update spoof mac in TL"));
return -EINVAL;
}
csr_scan->scan_randomize = true;
return 0;
}
/*
* If wiphy does not support cfg80211 scan randomization then scan
* will be randomized using the vendor MAC OUI.
*/
if (!hdd_ctx->spoofMacAddr.isEnabled)
return 0;
hddLog(VOS_TRACE_LEVEL_INFO,
FL("MAC Spoofing enabled for current scan and spoof addr is:"
MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(hdd_ctx->spoofMacAddr.randomMacAddr.bytes));
/* Updating SelfSta Mac Addr in TL which will be used to get staidx
* to fill TxBds for probe request during current scan
*/
vos_status = WLANTL_updateSpoofMacAddr(hdd_ctx->pvosContext,
&hdd_ctx->spoofMacAddr.randomMacAddr, &adapter->macAddressCurrent);
if(vos_status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to update spoof mac in TL"));
return -EINVAL;
}
csr_scan->scan_randomize = true;
return 0;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_scan
* this scan respond to scan trigger and update cfg80211 scan database
* later, scan dump command can be used to recieve scan results
*/
int __wlan_hdd_cfg80211_scan( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
hdd_wext_state_t *pwextBuf = NULL;
hdd_config_t *cfg_param = NULL;
tCsrScanRequest scanRequest;
tANI_U8 *channelList = NULL, i;
v_U32_t scanId = 0;
int status;
hdd_scaninfo_t *pScanInfo = NULL;
v_U8_t* pP2pIe = NULL;
int ret = 0;
v_U8_t *pWpsIe=NULL;
bool is_p2p_scan = false;
v_U8_t curr_session_id;
scan_reject_states curr_reason;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct net_device *dev = NULL;
if (NULL == request)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: scan req param null", __func__);
return -EINVAL;
}
dev = request->wdev->netdev;
#endif
pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
pwextBuf = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
ENTER();
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (NULL == pwextBuf)
{
hddLog (VOS_TRACE_LEVEL_ERROR, "%s ERROR: invalid WEXT state\n",
__func__);
return -EIO;
}
cfg_param = pHddCtx->cfg_ini;
pScanInfo = &pHddCtx->scan_info;
#ifdef WLAN_BTAMP_FEATURE
//Scan not supported when AMP traffic is on.
if (VOS_TRUE == WLANBAP_AmpSessionOn())
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: No scanning when AMP is on", __func__);
return -EOPNOTSUPP;
}
#endif
//Scan on any other interface is not supported.
if (pAdapter->device_mode == WLAN_HDD_SOFTAP)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Not scanning on device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
return -EOPNOTSUPP;
}
if (pAdapter->device_mode == WLAN_HDD_MONITOR) {
hddLog(LOGE, FL("Scan is not supported for monitor adapter"));
return -EOPNOTSUPP;
}
if (TRUE == pScanInfo->mScanPending)
{
if ( MAX_PENDING_LOG > pScanInfo->mScanPendingCounter++ )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: mScanPending is TRUE", __func__);
}
return -EBUSY;
}
// Don't allow scan if PNO scan is going on.
if (pHddCtx->isPnoEnable)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pno scan in progress"));
return -EBUSY;
}
//Don't Allow Scan and return busy if Remain On
//Channel and action frame is pending
//Otherwise Cancel Remain On Channel and allow Scan
//If no action frame pending
if (0 != wlan_hdd_check_remain_on_channel(pAdapter))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Remain On Channel Pending", __func__);
return -EBUSY;
}
if (mutex_lock_interruptible(&pHddCtx->tmInfo.tmOperationLock))
{
VOS_TRACE(VOS_MODULE_ID_HDD,VOS_TRACE_LEVEL_ERROR,
"%s: Acquire lock fail", __func__);
return -EAGAIN;
}
if (TRUE == pHddCtx->tmInfo.tmAction.enterImps)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: MAX TM Level Scan not allowed", __func__);
mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
return -EBUSY;
}
mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
/* Check if scan is allowed at this point of time.
*/
if (TRUE == pHddCtx->btCoexModeSet)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("BTCoex Mode operation in progress"));
return -EBUSY;
}
if (hdd_isConnectionInProgress(pHddCtx, &curr_session_id, &curr_reason))
{
if (!(pHddCtx->scan_reject_cnt % HDD_SCAN_REJECT_RATE_LIMIT))
hddLog(LOGE, FL("Scan not allowed Session %d reason %d"),
curr_session_id, curr_reason);
if (pHddCtx->last_scan_reject_session_id != curr_session_id ||
pHddCtx->last_scan_reject_reason != curr_reason ||
!pHddCtx->last_scan_reject_timestamp)
{
pHddCtx->last_scan_reject_session_id = curr_session_id;
pHddCtx->last_scan_reject_reason = curr_reason;
pHddCtx->last_scan_reject_timestamp =
jiffies + msecs_to_jiffies(SCAN_REJECT_THRESHOLD_TIME);
pHddCtx->scan_reject_cnt = 0;
}
else
{
pHddCtx->scan_reject_cnt++;
if ((pHddCtx->scan_reject_cnt >=
SCAN_REJECT_THRESHOLD) &&
vos_system_time_after(jiffies,
pHddCtx->last_scan_reject_timestamp))
{
hddLog(LOGE, FL("Session %d reason %d reject cnt %d reject timestamp %lu jiffies %lu"),
curr_session_id, curr_reason, pHddCtx->scan_reject_cnt,
pHddCtx->last_scan_reject_timestamp, jiffies);
pHddCtx->last_scan_reject_timestamp = 0;
pHddCtx->scan_reject_cnt = 0;
if (pHddCtx->cfg_ini->enableFatalEvent)
vos_fatal_event_logs_req(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_SCAN_NOT_ALLOWED,
FALSE, FALSE);
else
{
hddLog(LOGE, FL("Triggering SSR"));
vos_wlanRestart(VOS_SCAN_REQ_EXPIRED);
}
}
}
return -EBUSY;
}
pHddCtx->last_scan_reject_timestamp = 0;
pHddCtx->last_scan_reject_session_id = 0xFF;
pHddCtx->last_scan_reject_reason = 0;
pHddCtx->scan_reject_cnt = 0;
vos_mem_zero( &scanRequest, sizeof(scanRequest));
/* Even though supplicant doesn't provide any SSIDs, n_ssids is set to 1.
* Becasue of this, driver is assuming that this is not wildcard scan and so
* is not aging out the scan results.
*/
if ((request->ssids) && (request->n_ssids == 1) &&
('\0' == request->ssids->ssid[0])) {
request->n_ssids = 0;
}
if ((request->ssids) && (0 < request->n_ssids))
{
tCsrSSIDInfo *SsidInfo;
int j;
scanRequest.SSIDs.numOfSSIDs = request->n_ssids;
/* Allocate num_ssid tCsrSSIDInfo structure */
SsidInfo = scanRequest.SSIDs.SSIDList =
( tCsrSSIDInfo *)vos_mem_malloc(
request->n_ssids*sizeof(tCsrSSIDInfo));
if(NULL == scanRequest.SSIDs.SSIDList)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: memory alloc failed SSIDInfo buffer", __func__);
return -ENOMEM;
}
/* copy all the ssid's and their length */
for(j = 0; j < request->n_ssids; j++, SsidInfo++)
{
/* get the ssid length */
SsidInfo->SSID.length = request->ssids[j].ssid_len;
vos_mem_copy(SsidInfo->SSID.ssId, &request->ssids[j].ssid[0],
SsidInfo->SSID.length);
SsidInfo->SSID.ssId[SsidInfo->SSID.length] = '\0';
hddLog(VOS_TRACE_LEVEL_INFO, "SSID number %d: %s",
j, SsidInfo->SSID.ssId);
}
/* set the scan type to active */
scanRequest.scanType = eSIR_ACTIVE_SCAN;
}
else if(WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SCAN,
pAdapter->sessionId, 0));
/* set the scan type to active */
scanRequest.scanType = eSIR_ACTIVE_SCAN;
}
else
{
/*Set the scan type to default type, in this case it is ACTIVE*/
scanRequest.scanType = pScanInfo->scan_mode;
}
scanRequest.minChnTime = cfg_param->nActiveMinChnTime;
scanRequest.maxChnTime = cfg_param->nActiveMaxChnTime;
csr_scan_request_assign_bssid(&scanRequest, request);
/* set BSSType to default type */
scanRequest.BSSType = eCSR_BSS_TYPE_ANY;
/*TODO: scan the requested channels only*/
/*Right now scanning all the channels */
if (MAX_CHANNEL < request->n_channels)
{
hddLog(VOS_TRACE_LEVEL_WARN,
"No of Scan Channels exceeded limit: %d", request->n_channels);
request->n_channels = MAX_CHANNEL;
}
hddLog(VOS_TRACE_LEVEL_INFO,
"No of Scan Channels: %d", request->n_channels);
if( request->n_channels )
{
char chList [(request->n_channels*5)+1];
int len;
channelList = vos_mem_malloc( request->n_channels );
if( NULL == channelList )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: memory alloc failed channelList", __func__);
status = -ENOMEM;
goto free_mem;
}
for( i = 0, len = 0; i < request->n_channels ; i++ )
{
channelList[i] = request->channels[i]->hw_value;
len += snprintf(chList+len, 5, "%d ", channelList[i]);
}
hddLog(VOS_TRACE_LEVEL_INFO,
"Channel-List: %s ", chList);
}
scanRequest.ChannelInfo.numOfChannels = request->n_channels;
scanRequest.ChannelInfo.ChannelList = channelList;
/* set requestType to full scan */
scanRequest.requestType = eCSR_SCAN_REQUEST_FULL_SCAN;
/* if there is back to back scan happening in driver with in
* nDeferScanTimeInterval interval driver should defer new scan request
* and should provide last cached scan results instead of new channel list.
* This rule is not applicable if scan is p2p scan.
* This condition will work only in case when last request no of channels
* and channels are exactly same as new request.
* This should be done only in connected state
* Scan shouldn't be defered for WPS scan case.
*/
pWpsIe = wlan_hdd_get_wps_ie_ptr((v_U8_t*)request->ie,request->ie_len);
/* if wps ie is NULL , then only defer scan */
if ( pWpsIe == NULL &&
(VOS_STATUS_SUCCESS == hdd_is_any_session_connected(pHddCtx)))
{
if ( pScanInfo->last_scan_timestamp !=0 &&
((vos_timer_get_system_time() - pScanInfo->last_scan_timestamp ) < pHddCtx->cfg_ini->nDeferScanTimeInterval))
{
if ( request->no_cck == FALSE && scanRequest.ChannelInfo.numOfChannels != 1 &&
(pScanInfo->last_scan_numChannels == scanRequest.ChannelInfo.numOfChannels) &&
vos_mem_compare(pScanInfo->last_scan_channelList,
channelList, pScanInfo->last_scan_numChannels))
{
hddLog(VOS_TRACE_LEVEL_WARN,
" New and old station scan time differ is less then %u",
pHddCtx->cfg_ini->nDeferScanTimeInterval);
ret = wlan_hdd_cfg80211_update_bss((WLAN_HDD_GET_CTX(pAdapter))->wiphy,
pAdapter);
hddLog(VOS_TRACE_LEVEL_WARN,
"Return old cached scan as all channels and no of channels are same");
if (0 > ret)
hddLog(VOS_TRACE_LEVEL_INFO, "%s: NO SCAN result", __func__);
hdd_cfg80211_scan_done(pAdapter, request, eCSR_SCAN_SUCCESS);
status = eHAL_STATUS_SUCCESS;
goto free_mem;
}
}
}
/* Flush the scan results(only p2p beacons) for STA scan and P2P
* search (Flush on both full scan and social scan but not on single
* channel scan).P2P search happens on 3 social channels (1, 6, 11)
*/
/* Supplicant does single channel scan after 8-way handshake
* and in that case driver shoudnt flush scan results. If
* driver flushes the scan results here and unfortunately if
* the AP doesnt respond to our probe req then association
* fails which is not desired
*/
if ((request->n_ssids == 1)
&& (request->ssids != NULL)
&& vos_mem_compare(&request->ssids[0], "DIRECT-", 7))
is_p2p_scan = true;
if( is_p2p_scan ||
(request->n_channels != WLAN_HDD_P2P_SINGLE_CHANNEL_SCAN) )
{
hddLog(VOS_TRACE_LEVEL_DEBUG, "Flushing P2P Results");
sme_ScanFlushP2PResult( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId );
}
if( request->ie_len )
{
/* save this for future association (join requires this) */
/*TODO: Array needs to be converted to dynamic allocation,
* as multiple ie.s can be sent in cfg80211_scan_request structure
* CR 597966
*/
memset( &pScanInfo->scanAddIE, 0, sizeof(pScanInfo->scanAddIE) );
memcpy( pScanInfo->scanAddIE.addIEdata, request->ie, request->ie_len);
pScanInfo->scanAddIE.length = request->ie_len;
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode))
{
if (request->ie_len <= SIR_MAC_MAX_ADD_IE_LENGTH)
{
pwextBuf->roamProfile.nAddIEScanLength = request->ie_len;
memcpy( pwextBuf->roamProfile.addIEScan,
request->ie, request->ie_len);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "Scan Ie length is invalid:"
"%zu", request->ie_len);
}
}
scanRequest.uIEFieldLen = pScanInfo->scanAddIE.length;
scanRequest.pIEField = pScanInfo->scanAddIE.addIEdata;
pP2pIe = wlan_hdd_get_p2p_ie_ptr((v_U8_t*)request->ie,
request->ie_len);
if (pP2pIe != NULL)
{
#ifdef WLAN_FEATURE_P2P_DEBUG
if (((globalP2PConnectionStatus == P2P_GO_NEG_COMPLETED) ||
(globalP2PConnectionStatus == P2P_GO_NEG_PROCESS)) &&
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
{
globalP2PConnectionStatus = P2P_CLIENT_CONNECTING_STATE_1;
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P State] Changing state from "
"Go nego completed to Connection is started");
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P]P2P Scanning is started "
"for 8way Handshake");
}
else if((globalP2PConnectionStatus == P2P_CLIENT_DISCONNECTED_STATE) &&
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
{
globalP2PConnectionStatus = P2P_CLIENT_CONNECTING_STATE_2;
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P State] Changing state from "
"Disconnected state to Connection is started");
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P]P2P Scanning is started "
"for 4way Handshake");
}
#endif
/* no_cck will be set during p2p find to disable 11b rates */
if(TRUE == request->no_cck)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: This is a P2P Search", __func__);
scanRequest.p2pSearch = 1;
if( request->n_channels == WLAN_HDD_P2P_SOCIAL_CHANNELS )
{
/* set requestType to P2P Discovery */
scanRequest.requestType = eCSR_SCAN_P2P_DISCOVERY;
}
/*
Skip Dfs Channel in case of P2P Search
if it is set in ini file
*/
if(cfg_param->skipDfsChnlInP2pSearch)
{
scanRequest.skipDfsChnlInP2pSearch = 1;
}
else
{
scanRequest.skipDfsChnlInP2pSearch = 0;
}
}
}
}
INIT_COMPLETION(pScanInfo->scan_req_completion_event);
#ifdef FEATURE_WLAN_TDLS
/* if tdls disagree scan right now, return immediately.
tdls will schedule the scan when scan is allowed. (return SUCCESS)
or will reject the scan if any TDLS is in progress. (return -EBUSY)
*/
status = wlan_hdd_tdls_scan_callback (pAdapter,
wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
dev,
#endif
request);
if (status <= 0)
{
if (!status)
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: TDLS in progress."
"scan rejected %d", __func__, status);
else
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: TDLS teardown is ongoing %d",
__func__, status);
hdd_wlan_block_scan_by_tdls();
goto free_mem;
}
#endif
/* acquire the wakelock to avoid the apps suspend during the scan. To
* address the following issues.
* 1) Disconnected scenario: we are not allowing the suspend as WLAN is not in
* BMPS/IMPS this result in android trying to suspend aggressively and backing off
* for long time, this result in apps running at full power for long time.
* 2) Connected scenario: If we allow the suspend during the scan, RIVA will
* be stuck in full power because of resume BMPS
*/
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_SCAN);
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"requestType %d, scanType %d, minChnTime %d, maxChnTime %d,"
"p2pSearch %d, skipDfsChnlInP2pSearch %d",
scanRequest.requestType, scanRequest.scanType,
scanRequest.minChnTime, scanRequest.maxChnTime,
scanRequest.p2pSearch, scanRequest.skipDfsChnlInP2pSearch);
ret = hdd_spoof_scan(wiphy, pAdapter, request, is_p2p_scan, &scanRequest);
if(ret) {
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_SCAN);
status = -EFAULT;
#ifdef FEATURE_WLAN_TDLS
wlan_hdd_tdls_scan_done_callback(pAdapter);
#endif
goto free_mem;
}
wlan_hdd_get_frame_logs(pAdapter, WLAN_HDD_GET_FRAME_LOG_CMD_CLEAR);
status = sme_ScanRequest( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &scanRequest, &scanId,
&hdd_cfg80211_scan_done_callback, dev );
if (eHAL_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_ScanRequest returned error %d", __func__, status);
complete(&pScanInfo->scan_req_completion_event);
if(eHAL_STATUS_RESOURCES == status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: HO is in progress."
"So defer the scan by informing busy",__func__);
status = -EBUSY;
} else {
status = -EIO;
}
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_SCAN);
#ifdef FEATURE_WLAN_TDLS
wlan_hdd_tdls_scan_done_callback(pAdapter);
#endif
goto free_mem;
}
pScanInfo->mScanPending = TRUE;
pScanInfo->sessionId = pAdapter->sessionId;
pAdapter->request = request;
pScanInfo->scanId = scanId;
pScanInfo->no_cck = request->no_cck;
pHddCtx->scan_info.last_scan_numChannels = request->n_channels;
for (i = 0; i < pHddCtx->scan_info.last_scan_numChannels; i++) {
pHddCtx->scan_info.last_scan_channelList[i] =
request->channels[i]->hw_value;
}
complete(&pScanInfo->scan_req_completion_event);
free_mem:
if( scanRequest.SSIDs.SSIDList )
{
vos_mem_free(scanRequest.SSIDs.SSIDList);
}
if( channelList )
vos_mem_free( channelList );
EXIT();
return status;
}
int wlan_hdd_cfg80211_scan( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_scan(wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
dev,
#endif
request);
vos_ssr_unprotect(__func__);
return ret;
}
void hdd_select_cbmode( hdd_adapter_t *pAdapter,v_U8_t operationChannel)
{
v_U8_t iniDot11Mode =
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->dot11Mode;
eHddDot11Mode hddDot11Mode = iniDot11Mode;
hddLog(LOG1, FL("Channel Bonding Mode Selected is %u"),
iniDot11Mode);
switch ( iniDot11Mode )
{
case eHDD_DOT11_MODE_AUTO:
case eHDD_DOT11_MODE_11ac:
case eHDD_DOT11_MODE_11ac_ONLY:
#ifdef WLAN_FEATURE_11AC
if ( sme_IsFeatureSupportedByDriver(DOT11AC) &&
sme_IsFeatureSupportedByFW(DOT11AC) )
hddDot11Mode = eHDD_DOT11_MODE_11ac;
else
hddDot11Mode = eHDD_DOT11_MODE_11n;
#else
hddDot11Mode = eHDD_DOT11_MODE_11n;
#endif
break;
case eHDD_DOT11_MODE_11n:
case eHDD_DOT11_MODE_11n_ONLY:
hddDot11Mode = eHDD_DOT11_MODE_11n;
break;
default:
hddDot11Mode = iniDot11Mode;
break;
}
#ifdef WLAN_FEATURE_AP_HT40_24G
if (operationChannel > SIR_11B_CHANNEL_END)
#endif
{
/* This call decides required channel bonding mode */
sme_SelectCBMode((WLAN_HDD_GET_CTX(pAdapter)->hHal),
hdd_cfg_xlate_to_csr_phy_mode(hddDot11Mode),
operationChannel, eHT_MAX_CHANNEL_WIDTH);
}
}
/*
* FUNCTION: wlan_hdd_cfg80211_connect_start
* This function is used to start the association process
*/
int wlan_hdd_cfg80211_connect_start( hdd_adapter_t *pAdapter,
const u8 *ssid, size_t ssid_len, const u8 *bssid,
const u8 *bssid_hint, u8 operatingChannel)
{
int status = 0;
hdd_wext_state_t *pWextState;
hdd_context_t *pHddCtx;
hdd_station_ctx_t *hdd_sta_ctx;
v_U32_t roamId;
tCsrRoamProfile *pRoamProfile;
eCsrAuthType RSNAuthType;
ENTER();
pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (status)
{
return status;
}
if (SIR_MAC_MAX_SSID_LENGTH < ssid_len)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wrong SSID len", __func__);
return -EINVAL;
}
pRoamProfile = &pWextState->roamProfile;
if (pRoamProfile)
{
hdd_station_ctx_t *pHddStaCtx;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
pHddStaCtx->get_mgmt_log_sent = FALSE;
wlan_hdd_get_frame_logs(pAdapter, WLAN_HDD_GET_FRAME_LOG_CMD_CLEAR);
if (HDD_WMM_USER_MODE_NO_QOS ==
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->WmmMode)
{
/*QoS not enabled in cfg file*/
pRoamProfile->uapsd_mask = 0;
}
else
{
/*QoS enabled, update uapsd mask from cfg file*/
pRoamProfile->uapsd_mask =
(WLAN_HDD_GET_CTX(pAdapter))->cfg_ini->UapsdMask;
}
pRoamProfile->SSIDs.numOfSSIDs = 1;
pRoamProfile->SSIDs.SSIDList->SSID.length = ssid_len;
vos_mem_zero(pRoamProfile->SSIDs.SSIDList->SSID.ssId,
sizeof(pRoamProfile->SSIDs.SSIDList->SSID.ssId));
vos_mem_copy((void *)(pRoamProfile->SSIDs.SSIDList->SSID.ssId),
ssid, ssid_len);
vos_mem_zero(pRoamProfile->BSSIDs.bssid, WNI_CFG_BSSID_LEN);
vos_mem_zero(pRoamProfile->bssid_hint, WNI_CFG_BSSID_LEN);
if (bssid)
{
pRoamProfile->BSSIDs.numOfBSSIDs = 1;
vos_mem_copy(pRoamProfile->BSSIDs.bssid, bssid,
WNI_CFG_BSSID_LEN);
/* Save BSSID in seperate variable as well, as RoamProfile
BSSID is getting zeroed out in the association process. And in
case of join failure we should send valid BSSID to supplicant
*/
vos_mem_copy(pWextState->req_bssId, bssid,
WNI_CFG_BSSID_LEN);
}
else if (bssid_hint)
{
/* Store bssid_hint to use in the scan filter. */
vos_mem_copy(pRoamProfile->bssid_hint, bssid_hint,
WNI_CFG_BSSID_LEN);
/*
* Save BSSID in seperate variable as well, as RoamProfile
* BSSID is getting zeroed out in the association process. And in
* case of join failure we should send valid BSSID to supplicant
*/
vos_mem_copy(pWextState->req_bssId, bssid_hint,
WNI_CFG_BSSID_LEN);
hddLog(LOG1, FL(" bssid_hint: "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(pRoamProfile->bssid_hint));
}
hddLog(LOG1, FL("Connect to SSID: %s opertating Channel: %u"),
pRoamProfile->SSIDs.SSIDList->SSID.ssId, operatingChannel);
if ((IW_AUTH_WPA_VERSION_WPA == pWextState->wpaVersion) ||
(IW_AUTH_WPA_VERSION_WPA2 == pWextState->wpaVersion))
{
/*set gen ie*/
hdd_SetGENIEToCsr(pAdapter, &RSNAuthType);
/*set auth*/
hdd_set_csr_auth_type(pAdapter, RSNAuthType);
}
#ifdef FEATURE_WLAN_WAPI
if (pAdapter->wapi_info.nWapiMode)
{
hddLog(LOG1, "%s: Setting WAPI AUTH Type and Encryption Mode values", __func__);
switch (pAdapter->wapi_info.wapiAuthMode)
{
case WAPI_AUTH_MODE_PSK:
{
hddLog(LOG1, "%s: WAPI AUTH TYPE: PSK: %d", __func__,
pAdapter->wapi_info.wapiAuthMode);
pRoamProfile->AuthType.authType[0] = eCSR_AUTH_TYPE_WAPI_WAI_PSK;
break;
}
case WAPI_AUTH_MODE_CERT:
{
hddLog(LOG1, "%s: WAPI AUTH TYPE: CERT: %d", __func__,
pAdapter->wapi_info.wapiAuthMode);
pRoamProfile->AuthType.authType[0] = eCSR_AUTH_TYPE_WAPI_WAI_CERTIFICATE;
break;
}
} // End of switch
if ( pAdapter->wapi_info.wapiAuthMode == WAPI_AUTH_MODE_PSK ||
pAdapter->wapi_info.wapiAuthMode == WAPI_AUTH_MODE_CERT)
{
hddLog(LOG1, "%s: WAPI PAIRWISE/GROUP ENCRYPTION: WPI", __func__);
pRoamProfile->AuthType.numEntries = 1;
pRoamProfile->EncryptionType.numEntries = 1;
pRoamProfile->EncryptionType.encryptionType[0] = eCSR_ENCRYPT_TYPE_WPI;
pRoamProfile->mcEncryptionType.numEntries = 1;
pRoamProfile->mcEncryptionType.encryptionType[0] = eCSR_ENCRYPT_TYPE_WPI;
}
}
#endif /* FEATURE_WLAN_WAPI */
#ifdef WLAN_FEATURE_GTK_OFFLOAD
/* Initializing gtkOffloadReqParams */
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
{
memset(&pHddStaCtx->gtkOffloadReqParams, 0,
sizeof (tSirGtkOffloadParams));
pHddStaCtx->gtkOffloadReqParams.ulFlags = GTK_OFFLOAD_DISABLE;
}
#endif
pRoamProfile->csrPersona = pAdapter->device_mode;
if( operatingChannel )
{
pRoamProfile->ChannelInfo.ChannelList = &operatingChannel;
pRoamProfile->ChannelInfo.numOfChannels = 1;
}
else
{
pRoamProfile->ChannelInfo.ChannelList = NULL;
pRoamProfile->ChannelInfo.numOfChannels = 0;
}
if ( (WLAN_HDD_IBSS == pAdapter->device_mode) && operatingChannel)
{
hdd_select_cbmode(pAdapter,operatingChannel);
}
/*
* Change conn_state to connecting before sme_RoamConnect(),
* because sme_RoamConnect() has a direct path to call
* hdd_smeRoamCallback(), which will change the conn_state
* If direct path, conn_state will be accordingly changed
* to NotConnected or Associated by either
* hdd_AssociationCompletionHandler() or hdd_DisConnectHandler()
* 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_AssociationCompletionHandler, "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 (WLAN_HDD_INFRA_STATION == pAdapter->device_mode ||
WLAN_HDD_P2P_CLIENT == pAdapter->device_mode)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("Set HDD connState to eConnectionState_Connecting"));
hdd_connSetConnectionState(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter),
eConnectionState_Connecting);
vos_flush_delayed_work(&pHddCtx->ecsa_chan_change_work);
hdd_wait_for_ecsa_complete(pHddCtx);
}
status = sme_RoamConnect( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, pRoamProfile, &roamId);
if ((eHAL_STATUS_SUCCESS != status) &&
(WLAN_HDD_INFRA_STATION == pAdapter->device_mode ||
WLAN_HDD_P2P_CLIENT == pAdapter->device_mode))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_RoamConnect (session %d) failed with status %d. -> NotConnected"),
pAdapter->sessionId, status);
/* change back to NotAssociated */
hdd_connSetConnectionState(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter),
eConnectionState_NotConnected);
}
pRoamProfile->ChannelInfo.ChannelList = NULL;
pRoamProfile->ChannelInfo.numOfChannels = 0;
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: No valid Roam profile", __func__);
return -EINVAL;
}
EXIT();
return status;
}
/*
* FUNCTION: wlan_hdd_set_cfg80211_auth_type
* This function is used to set the authentication type (OPEN/SHARED).
*
*/
static int wlan_hdd_cfg80211_set_auth_type(hdd_adapter_t *pAdapter,
enum nl80211_auth_type auth_type)
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
ENTER();
/*set authentication type*/
switch (auth_type)
{
case NL80211_AUTHTYPE_AUTOMATIC:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: set authentication type to AUTOSWITCH", __func__);
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_AUTOSWITCH;
break;
case NL80211_AUTHTYPE_OPEN_SYSTEM:
#ifdef WLAN_FEATURE_VOWIFI_11R
case NL80211_AUTHTYPE_FT:
#endif /* WLAN_FEATURE_VOWIFI_11R */
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: set authentication type to OPEN", __func__);
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_OPEN_SYSTEM;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: set authentication type to SHARED", __func__);
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_SHARED_KEY;
break;
#ifdef FEATURE_WLAN_ESE
case NL80211_AUTHTYPE_NETWORK_EAP:
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: set authentication type to CCKM WPA", __func__);
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_CCKM_WPA;//eCSR_AUTH_TYPE_CCKM_RSN needs to be handled as well if required.
break;
#endif
case NL80211_AUTHTYPE_SAE:
hddLog(LOG1, "set authentication type to SAE");
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_SAE;
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unsupported authentication type %d", __func__,
auth_type);
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_UNKNOWN;
return -EINVAL;
}
pWextState->roamProfile.AuthType.authType[0] =
pHddStaCtx->conn_info.authType;
return 0;
}
/*
* FUNCTION: wlan_hdd_set_akm_suite
* This function is used to set the key mgmt type(PSK/8021x).
*
*/
static int wlan_hdd_set_akm_suite( hdd_adapter_t *pAdapter,
u32 key_mgmt
)
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
ENTER();
/* Should be in ieee802_11_defs.h */
#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
/*set key mgmt type*/
switch(key_mgmt)
{
case WLAN_AKM_SUITE_PSK:
case WLAN_AKM_SUITE_PSK_SHA256:
#ifdef WLAN_FEATURE_VOWIFI_11R
case WLAN_AKM_SUITE_FT_PSK:
#endif
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting key mgmt type to PSK",
__func__);
pWextState->authKeyMgmt |= IW_AUTH_KEY_MGMT_PSK;
break;
case WLAN_AKM_SUITE_8021X:
case WLAN_AKM_SUITE_8021X_SHA256:
#ifdef WLAN_FEATURE_VOWIFI_11R
case WLAN_AKM_SUITE_FT_8021X:
#endif
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting key mgmt type to 8021x",
__func__);
pWextState->authKeyMgmt |= IW_AUTH_KEY_MGMT_802_1X;
break;
#ifdef FEATURE_WLAN_ESE
#define WLAN_AKM_SUITE_CCKM 0x00409600 /* Should be in ieee802_11_defs.h */
#define IW_AUTH_KEY_MGMT_CCKM 8 /* Should be in linux/wireless.h */
case WLAN_AKM_SUITE_CCKM:
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting key mgmt type to CCKM",
__func__);
pWextState->authKeyMgmt |= IW_AUTH_KEY_MGMT_CCKM;
break;
#endif
#ifndef WLAN_AKM_SUITE_OSEN
#define WLAN_AKM_SUITE_OSEN 0x506f9a01 /* Should be in ieee802_11_defs.h */
case WLAN_AKM_SUITE_OSEN:
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting key mgmt type to OSEN",
__func__);
pWextState->authKeyMgmt |= IW_AUTH_KEY_MGMT_802_1X;
break;
#endif
case WLAN_AKM_SUITE_SAE:
hddLog(LOG1, "setting key mgmt type to SAE");
pWextState->authKeyMgmt |= IW_AUTH_KEY_MGMT_802_1X;
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Unsupported key mgmt type %d",
__func__, key_mgmt);
return -EINVAL;
}
return 0;
}
/*
* FUNCTION: wlan_hdd_cfg80211_set_cipher
* This function is used to set the encryption type
* (NONE/WEP40/WEP104/TKIP/CCMP).
*/
static int wlan_hdd_cfg80211_set_cipher( hdd_adapter_t *pAdapter,
u32 cipher,
bool ucast
)
{
eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE;
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
ENTER();
if (!cipher)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: received cipher %d - considering none",
__func__, 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;
#endif
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Unsupported cipher type %d",
__func__, cipher);
return -EOPNOTSUPP;
}
}
if (ucast)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting unicast cipher type to %d",
__func__, encryptionType);
pHddStaCtx->conn_info.ucEncryptionType = encryptionType;
pWextState->roamProfile.EncryptionType.numEntries = 1;
pWextState->roamProfile.EncryptionType.encryptionType[0] =
encryptionType;
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: setting mcast cipher type to %d",
__func__, encryptionType);
pHddStaCtx->conn_info.mcEncryptionType = encryptionType;
pWextState->roamProfile.mcEncryptionType.numEntries = 1;
pWextState->roamProfile.mcEncryptionType.encryptionType[0] = encryptionType;
}
return 0;
}
/*
* FUNCTION: wlan_hdd_cfg80211_set_ie
* This function is used to parse WPA/RSN IE's.
*/
int wlan_hdd_cfg80211_set_ie( hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *ie,
#else
u8 *ie,
#endif
size_t ie_len
)
{
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *genie = ie;
#else
u8 *genie = ie;
#endif
v_U16_t remLen = ie_len;
#ifdef FEATURE_WLAN_WAPI
v_U32_t akmsuite[MAX_NUM_AKM_SUITES];
u16 *tmp;
v_U16_t akmsuiteCount;
int *akmlist;
#endif
ENTER();
/* clear previous assocAddIE */
pWextState->assocAddIE.length = 0;
pWextState->roamProfile.bWPSAssociation = VOS_FALSE;
pWextState->roamProfile.bOSENAssociation = VOS_FALSE;
while (remLen >= 2)
{
v_U16_t eLen = 0;
v_U8_t elementId;
elementId = *genie++;
eLen = *genie++;
remLen -= 2;
/* Sanity check on eLen */
if (eLen > remLen) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid IE length[%d] for IE[0x%X]",
__func__, eLen, elementId);
VOS_ASSERT(0);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: IE[0x%X], LEN[%d]",
__func__, 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 */
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid WPA IE", __func__);
return -EINVAL;
}
else if (0 == memcmp(&genie[0], "\x00\x50\xf2\x04", 4))
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set WPS IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE. "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
// WSC IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.bWPSAssociation = VOS_TRUE;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.length;
}
else if (0 == memcmp(&genie[0], "\x00\x50\xf2", 3))
{
if (eLen > (MAX_WPA_RSN_IE_LEN - 2)) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid WPA RSN IE length[%d]",
__func__, eLen);
VOS_ASSERT(0);
return -EINVAL;
}
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set WPA IE (len %d)",__func__, eLen + 2);
memset( pWextState->WPARSNIE, 0, MAX_WPA_RSN_IE_LEN );
memcpy( pWextState->WPARSNIE, genie - 2, (eLen + 2) /*ie_len*/);
pWextState->roamProfile.pWPAReqIE = pWextState->WPARSNIE;
pWextState->roamProfile.nWPAReqIELength = eLen + 2;//ie_len;
}
else if ( (0 == memcmp(&genie[0], P2P_OUI_TYPE,
P2P_OUI_TYPE_SIZE)))
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set P2P IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
// P2P IE is saved to Additional IE ; it should be accumulated to handle WPS IE + P2P IE
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.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 */
&& (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) )
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set WFD IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
// WFD IE is saved to Additional IE ; it should be accumulated to handle
// WPS IE + P2P IE + WFD IE
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.length;
}
#endif
/* Appending HS 2.0 Indication Element in Assiciation Request */
else if ( (0 == memcmp(&genie[0], HS20_OUI_TYPE,
HS20_OUI_TYPE_SIZE)) )
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set HS20 IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.length;
}
/* Appending OSEN Information Element in Assiciation Request */
else if ( (0 == memcmp(&genie[0], OSEN_OUI_TYPE,
OSEN_OUI_TYPE_SIZE)) )
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set OSEN IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.bOSENAssociation = VOS_TRUE;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.length;
}
/* Update only for WPA IE */
if (!memcmp(genie, WPA_OUI_TYPE, WPA_OUI_TYPE_SIZE) &&
(WLAN_HDD_IBSS == pAdapter->device_mode)) {
/* populating as ADDIE in beacon frames */
if (ccmCfgSetStr(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, (u8 *)genie - 2, eLen + 2,
NULL, eANI_BOOLEAN_FALSE)== eHAL_STATUS_SUCCESS)
{
if (ccmCfgSetInt(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, 1,NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Coldn't pass "
"WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG to CCM");
}
}/* ccmCfgSetStr(,WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, , )*/
else
hddLog(LOGE,
"Could not pass on "
"WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA to CCM");
/* IBSS mode doesn't contain params->proberesp_ies still
beaconIE's need to be populated in probe response frames */
if ( (NULL != (genie - 2)) && (0 != eLen + 2) )
{
u16 rem_probe_resp_ie_len = eLen + 2;
u8 probe_rsp_ie_len[3] = {0};
u8 counter = 0;
/* Check Probe Resp Length if it is greater then 255 then
Store Probe Rsp IEs into WNI_CFG_PROBE_RSP_ADDNIE_DATA1
& WNI_CFG_PROBE_RSP_ADDNIE_DATA2 CFG Variable As We are
not able Store More then 255 bytes into One Variable */
while ((rem_probe_resp_ie_len > 0) && (counter < 3))
{
if (rem_probe_resp_ie_len > MAX_CFG_STRING_LEN)
{
probe_rsp_ie_len[counter++] = MAX_CFG_STRING_LEN;
rem_probe_resp_ie_len -= MAX_CFG_STRING_LEN;
}
else
{
probe_rsp_ie_len[counter++] = rem_probe_resp_ie_len;
rem_probe_resp_ie_len = 0;
}
}
rem_probe_resp_ie_len = 0;
if (probe_rsp_ie_len[0] > 0)
{
if (ccmCfgSetStr(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_DATA1,
(tANI_U8*)(genie - 2),
probe_rsp_ie_len[0], NULL,
eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass"
"on WNI_CFG_PROBE_RSP_ADDNIE_DATA1 to CCM");
}
rem_probe_resp_ie_len += probe_rsp_ie_len[0];
}
if (probe_rsp_ie_len[1] > 0)
{
if (ccmCfgSetStr(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_DATA2,
(tANI_U8*)(genie - (2 + rem_probe_resp_ie_len)),
probe_rsp_ie_len[1], NULL,
eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass"
"on WNI_CFG_PROBE_RSP_ADDNIE_DATA2 to CCM");
}
rem_probe_resp_ie_len += probe_rsp_ie_len[1];
}
if (probe_rsp_ie_len[2] > 0)
{
if (ccmCfgSetStr(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_DATA3,
(tANI_U8*)(genie - (2 + rem_probe_resp_ie_len)),
probe_rsp_ie_len[2], NULL,
eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass"
"on WNI_CFG_PROBE_RSP_ADDNIE_DATA3 to CCM");
}
rem_probe_resp_ie_len += probe_rsp_ie_len[2];
}
if (ccmCfgSetInt(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_FLAG, 1,NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
"Could not pass"
"on WNI_CFG_PROBE_RSP_ADDNIE_FLAG to CCM");
}
}
} /* end of if (WLAN_HDD_IBSS == pAdapter->device_mode) */
break;
case DOT11F_EID_RSN:
if (eLen > (MAX_WPA_RSN_IE_LEN - 2)) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid WPA RSN IE length[%d], exceeds %d bytes",
__func__, eLen, MAX_WPA_RSN_IE_LEN - 2);
VOS_ASSERT(0);
return -EINVAL;
}
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set RSN IE(len %d)",__func__, eLen + 2);
memset( pWextState->WPARSNIE, 0, MAX_WPA_RSN_IE_LEN );
memcpy( pWextState->WPARSNIE, genie - 2, (eLen + 2)/*ie_len*/);
pWextState->roamProfile.pRSNReqIE = pWextState->WPARSNIE;
pWextState->roamProfile.nRSNReqIELength = eLen + 2; //ie_len;
break;
/* Appending extended capabilities with Interworking or
* bsstransition bit set in Assoc Req.
*
* In assoc req this EXT Cap will only be taken into account if
* interworkingService or bsstransition bit is set to 1.
* Driver is only interested in interworkingService and
* bsstransition 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:
{
v_U16_t curAddIELen = pWextState->assocAddIE.length;
hddLog (VOS_TRACE_LEVEL_INFO, "%s Set Extended CAPS IE(len %d)",
__func__, eLen + 2);
if( SIR_MAC_MAX_ADD_IE_LENGTH < (pWextState->assocAddIE.length + eLen) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Cannot accommodate assocAddIE "
"Need bigger buffer space");
VOS_ASSERT(0);
return -ENOMEM;
}
memcpy( pWextState->assocAddIE.addIEdata + curAddIELen, genie - 2, eLen + 2);
pWextState->assocAddIE.length += eLen + 2;
pWextState->roamProfile.pAddIEAssoc = pWextState->assocAddIE.addIEdata;
pWextState->roamProfile.nAddIEAssocLength = pWextState->assocAddIE.length;
break;
}
#ifdef FEATURE_WLAN_WAPI
case WLAN_EID_WAPI:
pAdapter->wapi_info.nWapiMode = 1; //Setting WAPI Mode to ON=1
hddLog(VOS_TRACE_LEVEL_INFO, "WAPI MODE IS %u",
pAdapter->wapi_info.nWapiMode);
tmp = (u16 *)ie;
tmp = tmp + 2; // Skip element Id and Len, Version
akmsuiteCount = WPA_GET_LE16(tmp);
tmp = tmp + 1;
akmlist = (int *)(tmp);
if(akmsuiteCount <= MAX_NUM_AKM_SUITES)
{
memcpy(akmsuite, akmlist, (4*akmsuiteCount));
}
else
{
hddLog(VOS_TRACE_LEVEL_FATAL, "Invalid akmSuite count");
VOS_ASSERT(0);
return -EINVAL;
}
if (WAPI_PSK_AKM_SUITE == akmsuite[0])
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: WAPI AUTH MODE SET TO PSK",
__func__);
pAdapter->wapi_info.wapiAuthMode = WAPI_AUTH_MODE_PSK;
}
if (WAPI_CERT_AKM_SUITE == akmsuite[0])
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: WAPI AUTH MODE SET TO CERTIFICATE",
__func__);
pAdapter->wapi_info.wapiAuthMode = WAPI_AUTH_MODE_CERT;
}
break;
#endif
default:
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s Set UNKNOWN IE %X", __func__, elementId);
/* when Unknown IE is received we should break and continue
* to the next IE in the buffer instead we were returning
* so changing this to break */
break;
}
genie += eLen;
remLen -= eLen;
}
EXIT();
return 0;
}
/*
* FUNCTION: hdd_isWPAIEPresent
* Parse the received IE to find the WPA IE
*
*/
static bool hdd_isWPAIEPresent(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
const u8 *ie,
#else
u8 *ie,
#endif
u8 ie_len)
{
v_U8_t eLen = 0;
v_U16_t remLen = ie_len;
v_U8_t elementId = 0;
while (remLen >= 2)
{
elementId = *ie++;
eLen = *ie++;
remLen -= 2;
if (eLen > remLen)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: IE length is wrong %d", __func__, 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;
}
/*
* FUNCTION: wlan_hdd_cfg80211_set_privacy
* This function is used to initialize the security
* parameters during connect operation.
*/
int wlan_hdd_cfg80211_set_privacy(hdd_adapter_t *pAdapter,
struct cfg80211_connect_params *req
)
{
int status = 0;
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
ENTER();
/*set wpa version*/
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_DISABLED;
if (req->crypto.wpa_versions)
{
if (NL80211_WPA_VERSION_1 == req->crypto.wpa_versions)
{
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_WPA;
}
else if (NL80211_WPA_VERSION_2 == req->crypto.wpa_versions)
{
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_WPA2;
}
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: set wpa version to %d", __func__,
pWextState->wpaVersion);
/*set authentication type*/
status = wlan_hdd_cfg80211_set_auth_type(pAdapter, req->auth_type);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to set authentication type ", __func__);
return status;
}
/*set key mgmt type*/
if (req->crypto.n_akm_suites)
{
status = wlan_hdd_set_akm_suite(pAdapter, req->crypto.akm_suites[0]);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to set akm suite",
__func__);
return status;
}
}
/*set pairwise cipher type*/
if (req->crypto.n_ciphers_pairwise)
{
status = wlan_hdd_cfg80211_set_cipher(pAdapter,
req->crypto.ciphers_pairwise[0], true);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to set unicast cipher type", __func__);
return status;
}
}
else
{
/*Reset previous cipher suite to none*/
status = wlan_hdd_cfg80211_set_cipher(pAdapter, 0, true);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to set unicast cipher type", __func__);
return status;
}
}
/*set group cipher type*/
status = wlan_hdd_cfg80211_set_cipher(pAdapter, req->crypto.cipher_group,
false);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to set mcast cipher type",
__func__);
return status;
}
#ifdef WLAN_FEATURE_11W
pWextState->roamProfile.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(pAdapter, req->ie, req->ie_len);
if ( 0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to parse the WPA/RSN IE",
__func__);
return status;
}
}
/*incase of WEP set default key information*/
if (req->key && req->key_len)
{
if ( (WLAN_CIPHER_SUITE_WEP40 == req->crypto.ciphers_pairwise[0])
|| (WLAN_CIPHER_SUITE_WEP104 == req->crypto.ciphers_pairwise[0])
)
{
if ( IW_AUTH_KEY_MGMT_802_1X
== (pWextState->authKeyMgmt & IW_AUTH_KEY_MGMT_802_1X ))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Dynamic WEP not supported",
__func__);
return -EOPNOTSUPP;
}
else
{
u8 key_len = req->key_len;
u8 key_idx = req->key_idx;
if ((eCSR_SECURITY_WEP_KEYSIZE_MAX_BYTES >= key_len)
&& (CSR_MAX_NUM_KEY > key_idx)
)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: setting default wep key, key_idx = %hu key_len %hu",
__func__, key_idx, key_len);
vos_mem_copy(
&pWextState->roamProfile.Keys.KeyMaterial[key_idx][0],
req->key, key_len);
pWextState->roamProfile.Keys.KeyLength[key_idx] =
(u8)key_len;
pWextState->roamProfile.Keys.defaultIndex = (u8)key_idx;
}
}
}
}
return status;
}
/*
* FUNCTION: wlan_hdd_try_disconnect
* This function is used to disconnect from previous
* connection
*/
int wlan_hdd_try_disconnect( hdd_adapter_t *pAdapter )
{
long ret = 0;
int status, result = 0;
hdd_station_ctx_t *pHddStaCtx;
eMib_dot11DesiredBssType connectedBssType;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_connGetConnectedBssType(pHddStaCtx,&connectedBssType );
if((eMib_dot11DesiredBssType_independent == connectedBssType) ||
(eConnectionState_Associated == pHddStaCtx->conn_info.connState) ||
(eConnectionState_Connecting == pHddStaCtx->conn_info.connState) ||
(eConnectionState_IbssConnected == pHddStaCtx->conn_info.connState))
{
/* Indicate disconnect to SME so that in-progress connection or preauth
* can be aborted
*/
sme_abortConnection(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId);
spin_lock_bh(&pAdapter->lock_for_active_session);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
{
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
}
spin_unlock_bh(&pAdapter->lock_for_active_session);
hdd_connSetConnectionState(pHddStaCtx,
eConnectionState_Disconnecting);
/* Issue disconnect to CSR */
INIT_COMPLETION(pAdapter->disconnect_comp_var);
status = sme_RoamDisconnect(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
if(eHAL_STATUS_CMD_NOT_QUEUED == status) {
hddLog(LOG1,
FL("Already disconnected or connect was in sme/roam pending list and removed by disconnect"));
} else if ( 0 != status ) {
hddLog(LOGE,
FL("csrRoamDisconnect failure, returned %d"),
(int)status );
result = -EINVAL;
goto disconnected;
}
ret = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!ret && ( eHAL_STATUS_CMD_NOT_QUEUED != status)) {
hddLog(LOGE,
"%s: Failed to disconnect, timed out", __func__);
result = -ETIMEDOUT;
}
}
else if(eConnectionState_Disconnecting == pHddStaCtx->conn_info.connState)
{
ret = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!ret)
{
hddLog(LOGE, FL("Failed to receive disconnect event"));
result = -ETIMEDOUT;
}
}
disconnected:
hddLog(LOG1,
FL("Set HDD connState to eConnectionState_NotConnected"));
pHddStaCtx->conn_info.connState = 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: success if reassociation is happening
* Error code if reassociation is not permitted or not happening
*/
#ifdef CFG80211_CONNECT_PREV_BSSID
static int wlan_hdd_reassoc_bssid_hint(hdd_adapter_t *adapter,
struct cfg80211_connect_params *req)
{
int status = -EPERM;
const uint8_t *bssid = NULL;
uint16_t channel = 0;
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) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("REASSOC Attempt on channel %d to "MAC_ADDRESS_STR),
channel, MAC_ADDR_ARRAY(bssid));
status = hdd_reassoc(adapter, bssid, channel,
CONNECT_CMD_USERSPACE);
}
return status;
}
#else
static int wlan_hdd_reassoc_bssid_hint(hdd_adapter_t *adapter,
struct cfg80211_connect_params *req)
{
return -EPERM;
}
#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(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
struct cfg80211_connect_params *req)
{
hdd_wext_state_t *wext_state = WLAN_HDD_GET_WEXT_STATE_PTR(adapter);
tCsrRoamProfile *roam_profile;
roam_profile = &wext_state->roamProfile;
roam_profile->force_24ghz_in_ht20 = false;
if (hdd_ctx->cfg_ini->override_ht20_40_24g &&
!(req->ht_capa.cap_info &
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
roam_profile->force_24ghz_in_ht20 = true;
hddLog(LOG1, FL("req->ht_capa.cap_info %x override_ht20_40_24g %d"),
req->ht_capa.cap_info, hdd_ctx->cfg_ini->override_ht20_40_24g);
}
#else
static inline void wlan_hdd_check_ht20_ht40_ind(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
struct cfg80211_connect_params *req)
{
hdd_wext_state_t *wext_state = WLAN_HDD_GET_WEXT_STATE_PTR(adapter);
tCsrRoamProfile *roam_profile;
roam_profile = &wext_state->roamProfile;
roam_profile->force_24ghz_in_ht20 = false;
}
#endif
/*
* FUNCTION: __wlan_hdd_cfg80211_connect
* This function is used to start the association process
*/
static int __wlan_hdd_cfg80211_connect( struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *req
)
{
int status;
u16 channel;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) || \
defined(CFG80211_BSSID_HINT_BACKPORT)
const u8 *bssid_hint = req->bssid_hint;
#else
const u8 *bssid_hint = NULL;
#endif
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( ndev );
VOS_STATUS exitbmpsStatus = VOS_STATUS_E_INVAL;
hdd_context_t *pHddCtx = NULL;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CONNECT,
pAdapter->sessionId, pAdapter->device_mode));
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: device_mode = %s (%d)", __func__,
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is null", __func__);
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (wlan_hdd_check_and_stop_mon(pAdapter, true))
return -EINVAL;
status = wlan_hdd_reassoc_bssid_hint(pAdapter, req);
if (0 == status)
return status;
#ifdef WLAN_BTAMP_FEATURE
//Infra connect not supported when AMP traffic is on.
if( VOS_TRUE == WLANBAP_AmpSessionOn() )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: No connection when AMP is on", __func__);
return -ECONNREFUSED;
}
#endif
//If Device Mode is Station Concurrent Sessions Exit BMps
//P2P Mode will be taken care in Open/close adapter
if((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
(vos_concurrent_open_sessions_running())) {
exitbmpsStatus = hdd_disable_bmps_imps(pHddCtx,
WLAN_HDD_INFRA_STATION);
}
/*Try disconnecting if already in connected state*/
status = wlan_hdd_try_disconnect(pAdapter);
if ( 0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to disconnect the existing"
" connection"));
return -EALREADY;
}
/* Check for max concurrent connections after doing disconnect if any*/
if (vos_max_concurrent_connections_reached()) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
return -ECONNREFUSED;
}
/*initialise security parameters*/
status = wlan_hdd_cfg80211_set_privacy(pAdapter, req);
if ( 0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to set security params",
__func__);
return status;
}
if (pHddCtx->spoofMacAddr.isEnabled)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: MAC Spoofing enabled ", __func__);
/* Updating SelfSta Mac Addr in TL which will be used to get staidx
* to fill TxBds for probe request during SSID scan which may happen
* as part of connect command
*/
status = WLANTL_updateSpoofMacAddr(pHddCtx->pvosContext,
&pHddCtx->spoofMacAddr.randomMacAddr, &pAdapter->macAddressCurrent);
if (status != VOS_STATUS_SUCCESS)
return -ECONNREFUSED;
}
if (req->channel)
channel = req->channel->hw_value;
else
channel = 0;
/* Abort if any scan is going on */
status = wlan_hdd_scan_abort(pAdapter);
if (0 != status)
hddLog(VOS_TRACE_LEVEL_ERROR, FL("scan abort failed"));
wlan_hdd_check_ht20_ht40_ind(pHddCtx, pAdapter, req);
status = wlan_hdd_cfg80211_connect_start(pAdapter, req->ssid,
req->ssid_len, req->bssid,
bssid_hint, channel);
if (0 != status)
{
//ReEnable BMPS if disabled
if((VOS_STATUS_SUCCESS == exitbmpsStatus) &&
(NULL != pHddCtx))
{
if (pHddCtx->hdd_wlan_suspended)
{
hdd_set_pwrparams(pHddCtx);
}
//ReEnable Bmps and Imps back
hdd_enable_bmps_imps(pHddCtx);
}
hddLog(VOS_TRACE_LEVEL_ERROR, FL("connect failed"));
return status;
}
pHddCtx->isAmpAllowed = VOS_FALSE;
EXIT();
return status;
}
static int wlan_hdd_cfg80211_connect( struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *req)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_connect(wiphy, ndev, req);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_disconnect
* This function is used to issue a disconnect request to SME
*/
static int __wlan_hdd_cfg80211_disconnect( struct wiphy *wiphy,
struct net_device *dev,
u16 reason
)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
int status;
tCsrRoamProfile *pRoamProfile;
hdd_station_ctx_t *pHddStaCtx;
hdd_context_t *pHddCtx;
#ifdef FEATURE_WLAN_TDLS
tANI_U8 staIdx;
#endif
ENTER();
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (!pHddStaCtx) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD STA context is NULL"));
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
pRoamProfile = &(WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter))->roamProfile;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DISCONNECT,
pAdapter->sessionId, reason));
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s(%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Disconnect called with reason code %d",
__func__, reason);
if (NULL != pRoamProfile)
{
/*issue disconnect request to SME, if station is in connected state*/
if ((pHddStaCtx->conn_info.connState == eConnectionState_Associated) ||
(pHddStaCtx->conn_info.connState == eConnectionState_Connecting))
{
eCsrRoamDisconnectReason reasonCode =
eCSR_DISCONNECT_REASON_UNSPECIFIED;
hdd_scaninfo_t *pScanInfo;
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:
case WLAN_REASON_DEAUTH_LEAVING:
reasonCode = eCSR_DISCONNECT_REASON_DEAUTH;
break;
default:
reasonCode = eCSR_DISCONNECT_REASON_UNSPECIFIED;
break;
}
pScanInfo = &pHddCtx->scan_info;
if (pScanInfo->mScanPending)
{
hddLog(VOS_TRACE_LEVEL_INFO, "Disconnect is in progress, "
"Aborting Scan");
hdd_abort_mac_scan(pHddCtx, pScanInfo->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
wlan_hdd_cancel_existing_remain_on_channel(pAdapter);
#ifdef FEATURE_WLAN_TDLS
/* First clean up the tdls peers if any */
for (staIdx = 0 ; staIdx < HDD_MAX_NUM_TDLS_STA; staIdx++)
{
if ((pHddCtx->tdlsConnInfo[staIdx].sessionId == pAdapter->sessionId) &&
(pHddCtx->tdlsConnInfo[staIdx].staId))
{
uint8 *mac;
mac = pHddCtx->tdlsConnInfo[staIdx].peerMac.bytes;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: call sme_DeleteTdlsPeerSta staId %d sessionId %d " MAC_ADDRESS_STR,
__func__, pHddCtx->tdlsConnInfo[staIdx].staId, pAdapter->sessionId,
MAC_ADDR_ARRAY(mac));
status = sme_DeleteTdlsPeerSta(
WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
mac);
if (status != eHAL_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to delete TDLS peer STA"));
return -EPERM;
}
}
}
#endif
hddLog(LOG1, FL("Disconnecting with reasoncode:%u connState %d"),
reasonCode,
pHddStaCtx->conn_info.connState);
status = wlan_hdd_disconnect(pAdapter, reasonCode);
if ( 0 != status )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s wlan_hdd_disconnect failure, returned %d",
__func__, (int)status );
return -EINVAL;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unexpected cfg disconnect API"
"called while in %d state", __func__,
pHddStaCtx->conn_info.connState);
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: No valid roam profile", __func__);
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_disconnect( struct wiphy *wiphy,
struct net_device *dev,
u16 reason
)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_disconnect(wiphy, dev, reason);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: wlan_hdd_cfg80211_set_privacy_ibss
* This function is used to initialize the security
* settings in IBSS mode.
*/
static int wlan_hdd_cfg80211_set_privacy_ibss(
hdd_adapter_t *pAdapter,
struct cfg80211_ibss_params *params
)
{
int status = 0;
tANI_U32 ret;
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
eCsrEncryptionType encryptionType = eCSR_ENCRYPT_TYPE_NONE;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
ENTER();
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_DISABLED;
vos_mem_zero(&pHddStaCtx->ibss_enc_key, sizeof(tCsrRoamSetKey));
if (params->ie_len && ( NULL != params->ie) )
{
if (wlan_hdd_cfg80211_get_ie_ptr (params->ie,
params->ie_len, WLAN_EID_RSN ))
{
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_WPA2;
encryptionType = eCSR_ENCRYPT_TYPE_AES;
}
else if ( hdd_isWPAIEPresent (params->ie, params->ie_len ))
{
tDot11fIEWPA dot11WPAIE;
tHalHandle halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
u8 *ie;
memset(&dot11WPAIE, 0, sizeof(dot11WPAIE));
ie = wlan_hdd_cfg80211_get_ie_ptr (params->ie,
params->ie_len, DOT11F_EID_WPA);
if ( NULL != ie )
{
pWextState->wpaVersion = IW_AUTH_WPA_VERSION_WPA;
if (ie[1] < DOT11F_IE_WPA_MIN_LEN ||
ie[1] > DOT11F_IE_WPA_MAX_LEN) {
hddLog(LOGE, FL("invalid ie len:%d"), ie[1]);
return -EINVAL;
}
// Unpack the WPA IE
//Skip past the EID byte and length byte - and four byte WiFi OUI
ret = dot11fUnpackIeWPA((tpAniSirGlobal) halHandle,
&ie[2+4],
ie[1] - 4,
&dot11WPAIE);
if (DOT11F_FAILED(ret))
{
hddLog(LOGE,
FL("unpack failed status:(0x%08x)"),
ret);
return -EINVAL;
}
/*Extract the multicast cipher, the encType for unicast
cipher for wpa-none is none*/
encryptionType =
hdd_TranslateWPAToCsrEncryptionType(dot11WPAIE.multicast_cipher);
}
}
status = wlan_hdd_cfg80211_set_ie(pAdapter, params->ie, params->ie_len);
if (0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to parse WPA/RSN IE",
__func__);
return status;
}
}
pWextState->roamProfile.AuthType.authType[0] =
pHddStaCtx->conn_info.authType =
eCSR_AUTH_TYPE_OPEN_SYSTEM;
if (params->privacy)
{
/* Security enabled IBSS, At this time there is no information available
* about the security paramters, 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;
}
VOS_TRACE (VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"encryptionType=%d", encryptionType);
pHddStaCtx->conn_info.ucEncryptionType = encryptionType;
pWextState->roamProfile.EncryptionType.numEntries = 1;
pWextState->roamProfile.EncryptionType.encryptionType[0] = encryptionType;
return status;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_join_ibss
* This function is used to create/join an IBSS
*/
static int __wlan_hdd_cfg80211_join_ibss( struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ibss_params *params
)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
tCsrRoamProfile *pRoamProfile;
int status;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tSirMacAddr bssid;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_JOIN_IBSS,
pAdapter->sessionId, pAdapter->device_mode));
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: device_mode = %s (%d)", __func__,
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (NULL == pWextState)
{
hddLog (VOS_TRACE_LEVEL_ERROR, "%s ERROR: Data Storage Corruption",
__func__);
return -EIO;
}
if (vos_max_concurrent_connections_reached()) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
return -ECONNREFUSED;
}
/*Try disconnecting if already in connected state*/
status = wlan_hdd_try_disconnect(pAdapter);
if ( 0 > status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to disconnect the existing"
" IBSS connection"));
return -EALREADY;
}
pRoamProfile = &pWextState->roamProfile;
if ( eCSR_BSS_TYPE_START_IBSS != pRoamProfile->BSSType )
{
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s Interface type is not set to IBSS", __func__);
return -EINVAL;
}
/* BSSID is provided by upper layers hence no need to AUTO generate */
if (NULL != params->bssid) {
if (ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IBSS_AUTO_BSSID, 0,
NULL, eANI_BOOLEAN_FALSE)==eHAL_STATUS_FAILURE) {
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s:ccmCfgStInt faild for WNI_CFG_IBSS_AUTO_BSSID", __func__);
return -EIO;
}
vos_mem_copy((v_U8_t *)bssid, (v_U8_t *)params->bssid, sizeof(bssid));
}
else if(pHddCtx->cfg_ini->isCoalesingInIBSSAllowed == 0)
{
if (ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IBSS_AUTO_BSSID, 0,
NULL, eANI_BOOLEAN_FALSE)==eHAL_STATUS_FAILURE)
{
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s:ccmCfgStInt faild for WNI_CFG_IBSS_AUTO_BSSID", __func__);
return -EIO;
}
vos_mem_copy((v_U8_t *)bssid,
(v_U8_t *)&pHddCtx->cfg_ini->IbssBssid.bytes[0],
sizeof(bssid));
}
/* Set Channel */
if (NULL !=
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0))
params->chandef.chan)
#else
params->channel)
#endif
{
u8 channelNum;
v_U32_t numChans = WNI_CFG_VALID_CHANNEL_LIST_LEN;
v_U8_t validChan[WNI_CFG_VALID_CHANNEL_LIST_LEN];
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
int indx;
/* Get channel number */
channelNum =
ieee80211_frequency_to_channel(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0))
params->chandef.chan->center_freq);
#else
params->channel->center_freq);
#endif
if (0 != ccmCfgGetStr(hHal, WNI_CFG_VALID_CHANNEL_LIST,
validChan, &numChans))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: No valid channel list",
__func__);
return -EOPNOTSUPP;
}
for (indx = 0; indx < numChans; indx++)
{
if (channelNum == validChan[indx])
{
break;
}
}
if (indx >= numChans)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Not valid Channel %d",
__func__, channelNum);
return -EINVAL;
}
/* Set the Operational Channel */
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: set channel %d", __func__,
channelNum);
pRoamProfile->ChannelInfo.numOfChannels = 1;
pHddStaCtx->conn_info.operationChannel = channelNum;
pRoamProfile->ChannelInfo.ChannelList =
&pHddStaCtx->conn_info.operationChannel;
}
/* Initialize security parameters */
status = wlan_hdd_cfg80211_set_privacy_ibss(pAdapter, params);
if (status < 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to set security parameters",
__func__);
return status;
}
/* Issue connect start */
status = wlan_hdd_cfg80211_connect_start(pAdapter, params->ssid,
params->ssid_len, (const u8 *)&bssid, NULL,
pHddStaCtx->conn_info.operationChannel);
if (0 > status)
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: connect failed", __func__);
EXIT();
return status;
}
static int wlan_hdd_cfg80211_join_ibss( struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_ibss_params *params
)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_join_ibss(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_leave_ibss
* This function is used to leave an IBSS
*/
static int __wlan_hdd_cfg80211_leave_ibss( struct wiphy *wiphy,
struct net_device *dev
)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
tCsrRoamProfile *pRoamProfile;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status;
eHalStatus hal_status;
#ifdef WLAN_FEATURE_RMC
tANI_U8 addIE[WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN] = {0};
#endif
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_LEAVE_IBSS,
pAdapter->sessionId, eCSR_DISCONNECT_REASON_IBSS_LEAVE));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %s (%d)", __func__,
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
if (NULL == pWextState)
{
hddLog (VOS_TRACE_LEVEL_ERROR, "%s ERROR: Data Storage Corruption",
__func__);
return -EIO;
}
pRoamProfile = &pWextState->roamProfile;
/* Issue disconnect only if interface type is set to IBSS */
if (eCSR_BSS_TYPE_START_IBSS != pRoamProfile->BSSType)
{
hddLog (VOS_TRACE_LEVEL_ERROR, "%s: BSS Type is not set to IBSS",
__func__);
return -EINVAL;
}
#ifdef WLAN_FEATURE_RMC
/* Clearing add IE of beacon */
if (ccmCfgSetStr(pHddCtx->hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, &addIE[0],
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN,
NULL, eANI_BOOLEAN_FALSE) != eHAL_STATUS_SUCCESS)
{
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s: unable to clear PROBE_RSP_BCN_ADDNIE_DATA", __func__);
return -EINVAL;
}
if (ccmCfgSetInt(pHddCtx->hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, 0, NULL,
eANI_BOOLEAN_FALSE) != eHAL_STATUS_SUCCESS)
{
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s: unable to clear WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG",
__func__);
return -EINVAL;
}
// Reset WNI_CFG_PROBE_RSP Flags
wlan_hdd_reset_prob_rspies(pAdapter);
if (ccmCfgSetInt(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_FLAG, 0,NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog (VOS_TRACE_LEVEL_ERROR,
"%s: unable to clear WNI_CFG_PROBE_RSP_ADDNIE_FLAG",
__func__);
return -EINVAL;
}
#endif
/* Issue Disconnect request */
INIT_COMPLETION(pAdapter->disconnect_comp_var);
hal_status = sme_RoamDisconnect(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_IBSS_LEAVE);
if (!HAL_STATUS_SUCCESS(hal_status)) {
hddLog(LOGE,
FL("sme_RoamDisconnect failed hal_status(%d)"),
hal_status);
return -EAGAIN;
}
status = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!status) {
hddLog(LOGE,
FL("wait on disconnect_comp_var failed"));
return -ETIMEDOUT;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_leave_ibss( struct wiphy *wiphy,
struct net_device *dev
)
{
int ret = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_leave_ibss(wiphy, dev);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_set_wiphy_params
* This function is used to set the phy parameters
* (RTS Threshold/FRAG Threshold/Retry Count etc ...)
*/
static int __wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy,
u32 changed)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
tHalHandle hHal = pHddCtx->hHal;
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_WIPHY_PARAMS,
NO_SESSION, wiphy->rts_threshold));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (changed & WIPHY_PARAM_RTS_THRESHOLD)
{
u16 rts_threshold = (wiphy->rts_threshold == -1) ?
WNI_CFG_RTS_THRESHOLD_STAMAX :
wiphy->rts_threshold;
if ((WNI_CFG_RTS_THRESHOLD_STAMIN > rts_threshold) ||
(WNI_CFG_RTS_THRESHOLD_STAMAX < rts_threshold))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid RTS Threshold value %hu",
__func__, rts_threshold);
return -EINVAL;
}
if (0 != ccmCfgSetInt(hHal, WNI_CFG_RTS_THRESHOLD,
rts_threshold, ccmCfgSetCallback,
eANI_BOOLEAN_TRUE))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ccmCfgSetInt failed for rts_threshold value %hu",
__func__, rts_threshold);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: set rts threshold %hu", __func__,
rts_threshold);
}
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
{
u16 frag_threshold = (wiphy->frag_threshold == -1) ?
WNI_CFG_FRAGMENTATION_THRESHOLD_STAMAX :
wiphy->frag_threshold;
if ((WNI_CFG_FRAGMENTATION_THRESHOLD_STAMIN > frag_threshold)||
(WNI_CFG_FRAGMENTATION_THRESHOLD_STAMAX < frag_threshold) )
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid frag_threshold value %hu", __func__,
frag_threshold);
return -EINVAL;
}
if (0 != ccmCfgSetInt(hHal, WNI_CFG_FRAGMENTATION_THRESHOLD,
frag_threshold, ccmCfgSetCallback,
eANI_BOOLEAN_TRUE))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ccmCfgSetInt failed for frag_threshold value %hu",
__func__, frag_threshold);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: set frag threshold %hu", __func__,
frag_threshold);
}
if ((changed & WIPHY_PARAM_RETRY_SHORT)
|| (changed & WIPHY_PARAM_RETRY_LONG))
{
u8 retry_value = (changed & WIPHY_PARAM_RETRY_SHORT) ?
wiphy->retry_short :
wiphy->retry_long;
if ((WNI_CFG_LONG_RETRY_LIMIT_STAMIN > retry_value) ||
(WNI_CFG_LONG_RETRY_LIMIT_STAMAX < retry_value))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Retry count %hu",
__func__, retry_value);
return -EINVAL;
}
if (changed & WIPHY_PARAM_RETRY_SHORT)
{
if (0 != ccmCfgSetInt(hHal, WNI_CFG_LONG_RETRY_LIMIT,
retry_value, ccmCfgSetCallback,
eANI_BOOLEAN_TRUE))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ccmCfgSetInt failed for long retry count %hu",
__func__, retry_value);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: set long retry count %hu",
__func__, retry_value);
}
else if (changed & WIPHY_PARAM_RETRY_SHORT)
{
if (0 != ccmCfgSetInt(hHal, WNI_CFG_SHORT_RETRY_LIMIT,
retry_value, ccmCfgSetCallback,
eANI_BOOLEAN_TRUE))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ccmCfgSetInt failed for short retry count %hu",
__func__, retry_value);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: set short retry count %hu",
__func__, retry_value);
}
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_set_wiphy_params(struct wiphy *wiphy,
u32 changed)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_wiphy_params(wiphy, changed);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_set_txpower
* This function is used to set the txpower
*/
static int __wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
struct wireless_dev *wdev,
#endif
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
enum tx_power_setting type,
#else
enum nl80211_tx_power_setting type,
#endif
int dbm)
{
hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy);
tHalHandle hHal = NULL;
tSirMacAddr bssid = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
tSirMacAddr selfMac = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_TXPOWER,
NO_SESSION, type ));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hHal = pHddCtx->hHal;
if (0 != ccmCfgSetInt(hHal, WNI_CFG_CURRENT_TX_POWER_LEVEL,
dbm, ccmCfgSetCallback,
eANI_BOOLEAN_TRUE))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ccmCfgSetInt failed for tx power %hu", __func__, dbm);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_MED, "%s: set tx power level %d dbm", __func__,
dbm);
switch(type)
{
case NL80211_TX_POWER_AUTOMATIC: /*automatically determine transmit power*/
/* Fall through */
case NL80211_TX_POWER_LIMITED: /*limit TX power by the mBm parameter*/
if( sme_SetMaxTxPower(hHal, bssid, selfMac, dbm) != eHAL_STATUS_SUCCESS )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Setting maximum tx power failed",
__func__);
return -EIO;
}
break;
case NL80211_TX_POWER_FIXED: /*fix TX power to the mBm parameter*/
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: NL80211_TX_POWER_FIXED not supported",
__func__);
return -EOPNOTSUPP;
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid power setting type %d",
__func__, type);
return -EIO;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_set_txpower(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
struct wireless_dev *wdev,
#endif
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
enum tx_power_setting type,
#else
enum nl80211_tx_power_setting type,
#endif
int dbm)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_txpower(wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
wdev,
#endif
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,35)
type,
#else
type,
#endif
dbm);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_get_txpower
* This function is used to read the txpower
*/
static int __wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
struct wireless_dev *wdev,
#endif
int *dbm)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy);
int status;
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
*dbm = 0;
return status;
}
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Not in station context " ,__func__);
return -ENOENT;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_TXPOWER,
pAdapter->sessionId, pAdapter->device_mode));
wlan_hdd_get_classAstats(pAdapter);
*dbm = pAdapter->hdd_stats.ClassA_stat.max_pwr;
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_get_txpower(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
struct wireless_dev *wdev,
#endif
int *dbm)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_txpower(wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
wdev,
#endif
dbm);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* wlan_hdd_fill_summary_stats() - populate station_info summary stats
* @stats: summary stats to use as a source
* @info: kernel station_info struct to use as a destination
*
* Return: None
*/
static void wlan_hdd_fill_summary_stats(tCsrSummaryStatsInfo *stats,
struct station_info *info)
{
int i;
info->rx_packets = stats->rx_frm_cnt;
info->tx_packets = 0;
info->tx_retries = 0;
info->tx_failed = 0;
for (i = 0; i < 4; ++i) {
info->tx_packets += stats->tx_frm_cnt[i];
info->tx_retries += stats->multiple_retry_cnt[i];
info->tx_failed += stats->fail_cnt[i];
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
info->filled |= STATION_INFO_TX_PACKETS |
STATION_INFO_TX_RETRIES |
STATION_INFO_TX_FAILED |
STATION_INFO_RX_PACKETS;
#else
info->filled |= BIT(NL80211_STA_INFO_TX_PACKETS) |
BIT(NL80211_STA_INFO_TX_RETRIES) |
BIT(NL80211_STA_INFO_TX_FAILED) |
BIT(NL80211_STA_INFO_RX_PACKETS);
#endif
}
/**
* wlan_hdd_sap_get_sta_rssi() - get RSSI of the SAP client
* @adapter: sap adapter pointer
* @staid: station id of the client
* @rssi: rssi value to fill
*
* Return: None
*/
void
wlan_hdd_sap_get_sta_rssi(hdd_adapter_t *adapter, uint8_t staid, s8 *rssi)
{
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(adapter))->pvosContext;
WLANTL_GetSAPStaRSSi(pVosContext, staid, rssi);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
static inline void wlan_hdd_fill_station_info_signal(struct station_info
*sinfo)
{
sinfo->filled |= STATION_INFO_SIGNAL;
}
#else
static inline void wlan_hdd_fill_station_info_signal(struct station_info
*sinfo)
{
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
}
#endif
/**
* wlan_hdd_get_sap_stats() - get aggregate SAP stats
* @adapter: sap adapter to get stats for
* @mac: mac address of the station
* @info: kernel station_info struct to populate
*
* Fetch the vdev-level aggregate stats for the given SAP adapter. This is to
* support "station dump" and "station get" for SAP vdevs, even though they
* aren't technically stations.
*
* Return: errno
*/
static int
wlan_hdd_get_sap_stats(hdd_adapter_t *adapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8* mac,
#else
u8* mac,
#endif
struct station_info *info)
{
v_MACADDR_t *peerMacAddr;
uint8_t staid;
VOS_STATUS status;
bool bc_mac_addr;
status = wlan_hdd_get_station_stats(adapter);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"Failed to get SAP stats; status:%d", status);
return 0;
}
peerMacAddr = (v_MACADDR_t *)mac;
bc_mac_addr = vos_is_macaddr_broadcast(peerMacAddr);
staid = hdd_sta_id_find_from_mac_addr(adapter, peerMacAddr);
hddLog(VOS_TRACE_LEVEL_INFO, "Get SAP stats for sta id:%d", staid);
if (staid < WLAN_MAX_STA_COUNT && !bc_mac_addr) {
wlan_hdd_sap_get_sta_rssi(adapter, staid, &info->signal);
wlan_hdd_fill_station_info_signal(info);
}
wlan_hdd_fill_summary_stats(&adapter->hdd_stats.summary_stat, info);
return 0;
}
static int __wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8* mac,
#else
u8* mac,
#endif
struct station_info *sinfo)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
int ssidlen = pHddStaCtx->conn_info.SSID.SSID.length;
tANI_U32 rate_flags;
hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy);
hdd_config_t *pCfg = pHddCtx->cfg_ini;
tANI_U8 OperationalRates[CSR_DOT11_SUPPORTED_RATES_MAX];
tANI_U32 ORLeng = CSR_DOT11_SUPPORTED_RATES_MAX;
tANI_U8 ExtendedRates[CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX];
tANI_U32 ERLeng = CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX;
tANI_U8 MCSRates[SIZE_OF_BASIC_MCS_SET];
tANI_U32 MCSLeng = SIZE_OF_BASIC_MCS_SET;
tANI_U16 maxRate = 0;
int8_t snr = 0;
tANI_U16 myRate;
tANI_U16 currentRate = 0;
tANI_U8 maxSpeedMCS = 0;
tANI_U8 maxMCSIdx = 0;
tANI_U8 rateFlag = 1;
tANI_U8 i, j, rssidx, mode=0;
tANI_U16 temp;
int status;
#ifdef WLAN_FEATURE_11AC
tANI_U32 vht_mcs_map;
eDataRate11ACMaxMcs vhtMaxMcs;
#endif /* WLAN_FEATURE_11AC */
ENTER();
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (pAdapter->device_mode == WLAN_HDD_SOFTAP)
return wlan_hdd_get_sap_stats(pAdapter, mac, sinfo);
if ((eConnectionState_Associated != pHddStaCtx->conn_info.connState) ||
(0 == ssidlen))
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Not associated or"
" Invalid ssidlen, %d", __func__, ssidlen);
/*To keep GUI happy*/
return 0;
}
if (VOS_TRUE == pHddStaCtx->hdd_ReassocScenario)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Roaming in progress, so unable to proceed this request", __func__);
/* return a cached value */
sinfo->signal = pAdapter->rssi;
return 0;
}
wlan_hdd_get_station_stats(pAdapter);
rate_flags = pAdapter->hdd_stats.ClassA_stat.tx_rate_flags;
wlan_hdd_get_rssi(pAdapter, &sinfo->signal);
wlan_hdd_get_snr(pAdapter, &snr);
pHddStaCtx->conn_info.signal = sinfo->signal;
pHddStaCtx->cache_conn_info.signal = sinfo->signal;
pHddStaCtx->conn_info.noise = pHddStaCtx->conn_info.signal - snr;
pHddStaCtx->cache_conn_info.noise = pHddStaCtx->conn_info.noise;
wlan_hdd_fill_station_info_signal(sinfo);
/*overwrite rate_flags if MAX link-speed need to be reported*/
if ((eHDD_LINK_SPEED_REPORT_MAX == pCfg->reportMaxLinkSpeed) ||
(eHDD_LINK_SPEED_REPORT_MAX_SCALED == pCfg->reportMaxLinkSpeed &&
sinfo->signal >= pCfg->linkSpeedRssiLow))
{
rate_flags = pAdapter->maxRateFlags;
}
//convert to the UI units of 100kbps
myRate = pAdapter->hdd_stats.ClassA_stat.tx_rate * 5;
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("RSSI %d, RLMS %u, rate %d, rssi high %d, rssi mid %d, rssi low %d, rate_flags 0x%x, MCS %d\n",
sinfo->signal,
pCfg->reportMaxLinkSpeed,
myRate,
(int) pCfg->linkSpeedRssiHigh,
(int) pCfg->linkSpeedRssiMid,
(int) pCfg->linkSpeedRssiLow,
(int) rate_flags,
(int) pAdapter->hdd_stats.ClassA_stat.mcs_index);
#endif //LINKSPEED_DEBUG_ENABLED
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || defined(WITH_BACKPORTS)
/* assume basic BW. anything else will override this later */
sinfo->txrate.bw = RATE_INFO_BW_20;
#endif
if (eHDD_LINK_SPEED_REPORT_ACTUAL != pCfg->reportMaxLinkSpeed)
{
// we do not want to necessarily report the current speed
if (eHDD_LINK_SPEED_REPORT_MAX == pCfg->reportMaxLinkSpeed)
{
// report the max possible speed
rssidx = 0;
}
else if (eHDD_LINK_SPEED_REPORT_MAX_SCALED == pCfg->reportMaxLinkSpeed)
{
// report the max possible speed with RSSI scaling
if (sinfo->signal >= pCfg->linkSpeedRssiHigh)
{
// report the max possible speed
rssidx = 0;
}
else if (sinfo->signal >= pCfg->linkSpeedRssiMid)
{
// report middle speed
rssidx = 1;
}
else if (sinfo->signal >= pCfg->linkSpeedRssiLow)
{
// report middle speed
rssidx = 2;
}
else
{
// report actual speed
rssidx = 3;
}
}
else
{
// unknown, treat as eHDD_LINK_SPEED_REPORT_MAX
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid value for reportMaxLinkSpeed: %u",
__func__, pCfg->reportMaxLinkSpeed);
rssidx = 0;
}
maxRate = 0;
/* Get Basic Rate Set */
if (0 != ccmCfgGetStr(WLAN_HDD_GET_HAL_CTX(pAdapter), WNI_CFG_OPERATIONAL_RATE_SET,
OperationalRates, &ORLeng))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: ccm api returned failure", __func__);
/*To keep GUI happy*/
return 0;
}
for (i = 0; i < ORLeng; i++)
{
for (j = 0; j < (sizeof(supported_data_rate) / sizeof(supported_data_rate[0])); j ++)
{
/* Validate Rate Set */
if (supported_data_rate[j].beacon_rate_index == (OperationalRates[i] & 0x7F))
{
currentRate = supported_data_rate[j].supported_rate[rssidx];
break;
}
}
/* Update MAX rate */
maxRate = (currentRate > maxRate)?currentRate:maxRate;
}
/* Get Extended Rate Set */
if (0 != ccmCfgGetStr(WLAN_HDD_GET_HAL_CTX(pAdapter), WNI_CFG_EXTENDED_OPERATIONAL_RATE_SET,
ExtendedRates, &ERLeng))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: ccm api returned failure", __func__);
/*To keep GUI happy*/
return 0;
}
for (i = 0; i < ERLeng; i++)
{
for (j = 0; j < (sizeof(supported_data_rate) / sizeof(supported_data_rate[0])); j ++)
{
if (supported_data_rate[j].beacon_rate_index == (ExtendedRates[i] & 0x7F))
{
currentRate = supported_data_rate[j].supported_rate[rssidx];
break;
}
}
/* Update MAX rate */
maxRate = (currentRate > maxRate)?currentRate:maxRate;
}
/* Get MCS Rate Set --
Only if we are always reporting max speed (or)
if we have good rssi */
if ((3 != rssidx) && !(rate_flags & eHAL_TX_RATE_LEGACY))
{
if (rate_flags & eHAL_TX_RATE_VHT80)
mode = 2;
else if (rate_flags & (eHAL_TX_RATE_VHT40 | eHAL_TX_RATE_HT40))
mode = 1;
else
mode = 0;
if (0 != ccmCfgGetStr(WLAN_HDD_GET_HAL_CTX(pAdapter), WNI_CFG_CURRENT_MCS_SET,
MCSRates, &MCSLeng))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: ccm api returned failure", __func__);
/*To keep GUI happy*/
return 0;
}
rateFlag = 0;
#ifdef WLAN_FEATURE_11AC
/* VHT80 rate has seperate rate table */
if (rate_flags & (eHAL_TX_RATE_VHT20|eHAL_TX_RATE_VHT40|eHAL_TX_RATE_VHT80))
{
ccmCfgGetInt(WLAN_HDD_GET_HAL_CTX(pAdapter), WNI_CFG_VHT_TX_MCS_MAP, &vht_mcs_map);
vhtMaxMcs = (eDataRate11ACMaxMcs)(vht_mcs_map & DATA_RATE_11AC_MCS_MASK );
if (rate_flags & eHAL_TX_RATE_SGI)
{
rateFlag |= 1;
}
if (DATA_RATE_11AC_MAX_MCS_7 == vhtMaxMcs)
{
maxMCSIdx = 7;
}
else if (DATA_RATE_11AC_MAX_MCS_8 == vhtMaxMcs)
{
maxMCSIdx = 8;
}
else if (DATA_RATE_11AC_MAX_MCS_9 == vhtMaxMcs)
{
//VHT20 is supporting 0~8
if (rate_flags & eHAL_TX_RATE_VHT20)
maxMCSIdx = 8;
else
maxMCSIdx = 9;
}
if (0 != rssidx)/*check for scaled */
{
//get middle rate MCS index if rssi=1/2
for (i=0; i <= maxMCSIdx; i++)
{
if (sinfo->signal <= rssiMcsTbl[mode][i])
{
maxMCSIdx = i;
break;
}
}
}
if (rate_flags & eHAL_TX_RATE_VHT80)
{
currentRate = supported_vht_mcs_rate[pAdapter->hdd_stats.ClassA_stat.mcs_index].supported_VHT80_rate[rateFlag];
maxRate = supported_vht_mcs_rate[maxMCSIdx].supported_VHT80_rate[rateFlag];
}
else if (rate_flags & eHAL_TX_RATE_VHT40)
{
currentRate = supported_vht_mcs_rate[pAdapter->hdd_stats.ClassA_stat.mcs_index].supported_VHT40_rate[rateFlag];
maxRate = supported_vht_mcs_rate[maxMCSIdx].supported_VHT40_rate[rateFlag];
}
else if (rate_flags & eHAL_TX_RATE_VHT20)
{
currentRate = supported_vht_mcs_rate[pAdapter->hdd_stats.ClassA_stat.mcs_index].supported_VHT20_rate[rateFlag];
maxRate = supported_vht_mcs_rate[maxMCSIdx].supported_VHT20_rate[rateFlag];
}
maxSpeedMCS = 1;
if (currentRate > maxRate)
{
maxRate = currentRate;
}
}
else
#endif /* WLAN_FEATURE_11AC */
{
if (rate_flags & eHAL_TX_RATE_HT40)
{
rateFlag |= 1;
}
if (rate_flags & eHAL_TX_RATE_SGI)
{
rateFlag |= 2;
}
temp = sizeof(supported_mcs_rate) / sizeof(supported_mcs_rate[0]);
if (rssidx == 1 || rssidx == 2)
{
//get middle rate MCS index if rssi=1/2
for (i=0; i <= 7; i++)
{
if (sinfo->signal <= rssiMcsTbl[mode][i])
{
temp = i+1;
break;
}
}
}
for (i = 0; i < MCSLeng; i++)
{
for (j = 0; j < temp; j++)
{
if (supported_mcs_rate[j].beacon_rate_index == MCSRates[i])
{
currentRate = supported_mcs_rate[j].supported_rate[rateFlag];
maxMCSIdx = supported_mcs_rate[j].beacon_rate_index;
break;
}
}
if ((j < temp) && (currentRate > maxRate))
{
maxRate = currentRate;
}
}
maxSpeedMCS = 1;
}
}
else if (!(rate_flags & eHAL_TX_RATE_LEGACY))
{
maxRate = myRate;
maxSpeedMCS = 1;
maxMCSIdx = pAdapter->hdd_stats.ClassA_stat.mcs_index;
}
// make sure we report a value at least as big as our current rate
if ((maxRate < myRate) || (0 == maxRate))
{
maxRate = myRate;
if (rate_flags & eHAL_TX_RATE_LEGACY)
{
maxSpeedMCS = 0;
}
else
{
maxSpeedMCS = 1;
maxMCSIdx = pAdapter->hdd_stats.ClassA_stat.mcs_index;
}
}
if (rate_flags & eHAL_TX_RATE_LEGACY)
{
sinfo->txrate.legacy = maxRate;
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting legacy rate %d\n", sinfo->txrate.legacy);
#endif //LINKSPEED_DEBUG_ENABLED
}
else
{
sinfo->txrate.mcs = maxMCSIdx;
#ifdef WLAN_FEATURE_11AC
sinfo->txrate.nss = 1;
if (rate_flags & eHAL_TX_RATE_VHT80)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || \
defined(WITH_BACKPORTS)
sinfo->txrate.bw = RATE_INFO_BW_80;
#else
sinfo->txrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
}
else if (rate_flags & eHAL_TX_RATE_VHT40)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || \
defined(WITH_BACKPORTS)
sinfo->txrate.bw = RATE_INFO_BW_40;
#else
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
else if (rate_flags & eHAL_TX_RATE_VHT20)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
}
#endif /* WLAN_FEATURE_11AC */
if (rate_flags & (eHAL_TX_RATE_HT20 | eHAL_TX_RATE_HT40))
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
if (rate_flags & eHAL_TX_RATE_HT40)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || \
defined(WITH_BACKPORTS)
sinfo->txrate.bw = RATE_INFO_BW_40;
#else
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
}
if (rate_flags & eHAL_TX_RATE_SGI)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
}
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting MCS rate %d flags %x\n",
sinfo->txrate.mcs,
sinfo->txrate.flags );
#endif //LINKSPEED_DEBUG_ENABLED
}
}
else
{
// report current rate instead of max rate
if (rate_flags & eHAL_TX_RATE_LEGACY)
{
//provide to the UI in units of 100kbps
sinfo->txrate.legacy = myRate;
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting actual legacy rate %d\n", sinfo->txrate.legacy);
#endif //LINKSPEED_DEBUG_ENABLED
}
else
{
//must be MCS
sinfo->txrate.mcs = pAdapter->hdd_stats.ClassA_stat.mcs_index;
#ifdef WLAN_FEATURE_11AC
sinfo->txrate.nss = 1;
if (rate_flags & eHAL_TX_RATE_VHT80)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
}
else
#endif /* WLAN_FEATURE_11AC */
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
}
if (rate_flags & eHAL_TX_RATE_SGI)
{
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
}
if (rate_flags & eHAL_TX_RATE_HT40)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || \
defined(WITH_BACKPORTS)
sinfo->txrate.bw = RATE_INFO_BW_40;
#else
sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
}
#ifdef WLAN_FEATURE_11AC
else if (rate_flags & eHAL_TX_RATE_VHT80)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) || \
defined(WITH_BACKPORTS)
sinfo->txrate.bw = RATE_INFO_BW_80;
#else
sinfo->txrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
}
#endif /* WLAN_FEATURE_11AC */
#ifdef LINKSPEED_DEBUG_ENABLED
pr_info("Reporting actual MCS rate %d flags %x\n",
sinfo->txrate.mcs,
sinfo->txrate.flags );
#endif //LINKSPEED_DEBUG_ENABLED
}
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
sinfo->filled |= STATION_INFO_TX_BITRATE;
#else
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
#endif
sinfo->tx_packets =
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[0] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[1] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[2] +
pAdapter->hdd_stats.summary_stat.tx_frm_cnt[3];
sinfo->tx_retries =
pAdapter->hdd_stats.summary_stat.retry_cnt[0] +
pAdapter->hdd_stats.summary_stat.retry_cnt[1] +
pAdapter->hdd_stats.summary_stat.retry_cnt[2] +
pAdapter->hdd_stats.summary_stat.retry_cnt[3];
sinfo->tx_failed =
pAdapter->hdd_stats.summary_stat.fail_cnt[0] +
pAdapter->hdd_stats.summary_stat.fail_cnt[1] +
pAdapter->hdd_stats.summary_stat.fail_cnt[2] +
pAdapter->hdd_stats.summary_stat.fail_cnt[3];
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && \
!defined(WITH_BACKPORTS)
sinfo->filled |=
STATION_INFO_RX_PACKETS |
STATION_INFO_TX_PACKETS |
STATION_INFO_TX_RETRIES |
STATION_INFO_TX_FAILED;
#else
sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
BIT(NL80211_STA_INFO_TX_PACKETS) |
BIT(NL80211_STA_INFO_TX_RETRIES) |
BIT(NL80211_STA_INFO_TX_FAILED);
#endif
sinfo->rx_packets = pAdapter->hdd_stats.summary_stat.rx_frm_cnt;
vos_mem_copy(&pHddStaCtx->conn_info.txrate,
&sinfo->txrate, sizeof(sinfo->txrate));
vos_mem_copy(&pHddStaCtx->cache_conn_info.txrate,
&sinfo->txrate, sizeof(sinfo->txrate));
if (rate_flags & eHAL_TX_RATE_LEGACY)
hddLog(LOG1, FL("Reporting RSSI:%d legacy rate %d pkt cnt tx %d rx %d"),
sinfo->signal, sinfo->txrate.legacy, sinfo->tx_packets,
sinfo->rx_packets);
else
hddLog(LOG1,
FL("Reporting RSSI:%d MCS rate %d flags 0x%x pkt cnt tx %d rx %d"),
sinfo->signal, sinfo->txrate.mcs, sinfo->txrate.flags,
sinfo->tx_packets, sinfo->rx_packets);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_GET_STA,
pAdapter->sessionId, maxRate));
EXIT();
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
static int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
const u8* mac, struct station_info *sinfo)
#else
static int wlan_hdd_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
u8* mac, struct station_info *sinfo)
#endif
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev, bool mode, int timeout)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
VOS_STATUS vos_status;
int status;
ENTER();
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Adapter is NULL", __func__);
return -ENODEV;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_POWER_MGMT,
pAdapter->sessionId, timeout));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if ((DRIVER_POWER_MODE_AUTO == !mode) &&
(TRUE == pHddCtx->hdd_wlan_suspended) &&
(pHddCtx->cfg_ini->fhostArpOffload) &&
(eConnectionState_Associated ==
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState))
{
hddLog(VOS_TRACE_LEVEL_INFO,
"offload: in cfg80211_set_power_mgmt, calling arp offload");
vos_status = hdd_conf_arp_offload(pAdapter, TRUE);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s:Failed to enable ARPOFFLOAD Feature %d",
__func__, vos_status);
}
}
/**The get power cmd from the supplicant gets updated by the nl only
*on successful execution of the function call
*we are oppositely mapped w.r.t mode in the driver
**/
vos_status = wlan_hdd_enter_bmps(pAdapter, !mode);
if (VOS_STATUS_E_FAILURE == vos_status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to enter bmps mode", __func__);
return -EINVAL;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev, bool mode, int timeout)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_power_mgmt(wiphy, dev, mode, timeout);
vos_ssr_unprotect(__func__);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int __wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index)
{
ENTER();
return 0;
}
static int wlan_hdd_set_default_mgmt_key(struct wiphy *wiphy,
struct net_device *netdev,
u8 key_index)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_set_default_mgmt_key(wiphy, netdev, key_index);
vos_ssr_unprotect(__func__);
return ret;
}
#endif //LINUX_VERSION_CODE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
static int __wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_txq_params *params)
{
ENTER();
return 0;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int __wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct ieee80211_txq_params *params)
{
ENTER();
return 0;
}
#endif //LINUX_VERSION_CODE
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
static int wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_txq_params *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_set_txq_params(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static int wlan_hdd_set_txq_params(struct wiphy *wiphy,
struct ieee80211_txq_params *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_set_txq_params(wiphy, params);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
static int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
struct tagCsrDelStaParams *pDelStaParams)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
VOS_STATUS vos_status;
int status;
v_U8_t staId;
v_CONTEXT_t pVosContext = NULL;
ptSapContext pSapCtx = NULL;
hdd_hostapd_state_t *hostap_state;
ENTER();
if ( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Adapter" ,__func__);
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DEL_STA,
pAdapter->sessionId, pAdapter->device_mode));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if ( (WLAN_HDD_SOFTAP == pAdapter->device_mode)
|| (WLAN_HDD_P2P_GO == pAdapter->device_mode)
)
{
hostap_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
pVosContext = ( WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
pSapCtx = VOS_GET_SAP_CB(pVosContext);
if(pSapCtx == NULL){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("psapCtx is NULL"));
return -ENOENT;
}
if (pHddCtx->cfg_ini->enable_sap_auth_offload)
{
VOS_TRACE(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO,
"Change reason code to eSIR_MAC_DISASSOC_LEAVING_BSS_REASON in sap auth offload");
pDelStaParams->reason_code = eSIR_MAC_DISASSOC_LEAVING_BSS_REASON;
}
if (vos_is_macaddr_broadcast((v_MACADDR_t *)pDelStaParams->peerMacAddr))
{
v_U16_t i;
for(i = 0; i < WLAN_MAX_STA_COUNT; i++)
{
if ((pSapCtx->aStaInfo[i].isUsed) &&
(!pSapCtx->aStaInfo[i].isDeauthInProgress))
{
vos_mem_copy(pDelStaParams->peerMacAddr,
pSapCtx->aStaInfo[i].macAddrSTA.bytes,
ETHER_ADDR_LEN);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Delete STA with MAC::"
MAC_ADDRESS_STR,
__func__,
MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
vos_event_reset(&hostap_state->sta_discon_event);
vos_status = hdd_softap_sta_deauth(pAdapter, pDelStaParams);
if (VOS_IS_STATUS_SUCCESS(vos_status))
{
pSapCtx->aStaInfo[i].isDeauthInProgress = TRUE;
vos_status =
vos_wait_single_event(
&hostap_state->sta_discon_event,
WLAN_WAIT_TIME_DISCONNECT);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE,"!!%s: ERROR: Deauth wait expired!!",
__func__);
}
}
}
}
else
{
vos_status = hdd_softap_GetStaId(pAdapter,
(v_MACADDR_t *)pDelStaParams->peerMacAddr, &staId);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Skip this DEL STA as this is not used::"
MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
return -ENOENT;
}
if( pSapCtx->aStaInfo[staId].isDeauthInProgress == TRUE)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Skip this DEL STA as deauth is in progress::"
MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
return -ENOENT;
}
pSapCtx->aStaInfo[staId].isDeauthInProgress = TRUE;
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Delete STA with MAC::"
MAC_ADDRESS_STR,
__func__,
MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
vos_event_reset(&hostap_state->sta_discon_event);
vos_status = hdd_softap_sta_deauth(pAdapter, pDelStaParams);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
pSapCtx->aStaInfo[staId].isDeauthInProgress = FALSE;
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: STA removal failed for ::"
MAC_ADDRESS_STR,
__func__,
MAC_ADDR_ARRAY(pDelStaParams->peerMacAddr));
return -ENOENT;
}
vos_status =
vos_wait_single_event(&hostap_state->sta_discon_event,
WLAN_WAIT_TIME_DISCONNECT);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE,"!!%s: ERROR: Deauth wait expired!!", __func__);
}
}
EXIT();
return 0;
}
#ifdef USE_CFG80211_DEL_STA_V2
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev,
struct station_del_parameters *param)
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev, const u8 *mac)
#else
int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy,
struct net_device *dev, u8 *mac)
#endif
#endif
{
int ret;
struct tagCsrDelStaParams delStaParams;
vos_ssr_protect(__func__);
#ifdef USE_CFG80211_DEL_STA_V2
if (NULL == param) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid argumet passed", __func__);
vos_ssr_unprotect(__func__);
return -EINVAL;
}
WLANSAP_PopulateDelStaParams(param->mac, param->reason_code,
param->subtype, &delStaParams);
#else
WLANSAP_PopulateDelStaParams(mac, eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
(SIR_MAC_MGMT_DEAUTH >> 4), &delStaParams);
#endif
ret = __wlan_hdd_cfg80211_del_station(wiphy, dev, &delStaParams);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int status = -EPERM;
#ifdef FEATURE_WLAN_TDLS
u32 mask, set;
ENTER();
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Adapter is NULL",__func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_ADD_STA,
pAdapter->sessionId, params->listen_interval));
mask = params->sta_flags_mask;
set = params->sta_flags_set;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: mask 0x%x set 0x%x " MAC_ADDRESS_STR,
__func__, mask, set, MAC_ADDR_ARRAY(mac));
if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
status = wlan_hdd_tdls_add_station(wiphy, dev, mac, 0, NULL);
}
}
#endif
EXIT();
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev, const u8 *mac,
struct station_parameters *params)
#else
static int wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev, u8 *mac, struct station_parameters *params)
#endif
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_add_station(wiphy, dev, mac, params);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_WLAN_LFR
static int __wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle halHandle;
eHalStatus result;
int status;
hdd_context_t *pHddCtx;
tPmkidCacheInfo pmk_id;
ENTER();
// Validate pAdapter
if ( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Adapter" ,__func__);
return -EINVAL;
}
if (!pmksa) {
hddLog(LOGE, FL("pmksa is NULL"));
return -EINVAL;
}
if (!pmksa->bssid || !pmksa->pmkid) {
hddLog(LOGE, FL("pmksa->bssid(%pK) or pmksa->pmkid(%pK) is NULL"),
pmksa->bssid, pmksa->pmkid);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_DEBUG, "%s: set PMKSA for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(pmksa->bssid));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
// Retrieve halHandle
halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
vos_mem_copy(pmk_id.BSSID, pmksa->bssid, ETHER_ADDR_LEN);
vos_mem_copy(pmk_id.PMKID, pmksa->pmkid, CSR_RSN_PMKID_SIZE);
/* Add to the PMKSA ID Cache in CSR */
result = sme_RoamSetPMKIDCache(halHandle,pAdapter->sessionId,
&pmk_id, 1, FALSE);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_PMKSA,
pAdapter->sessionId, result));
EXIT();
return HAL_STATUS_SUCCESS(result) ? 0 : -EINVAL;
}
static int wlan_hdd_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_pmksa(wiphy, dev, pmksa);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle halHandle;
hdd_context_t *pHddCtx;
int status = 0;
ENTER();
/* Validate pAdapter */
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Adapter" ,__func__);
return -EINVAL;
}
if (!pmksa) {
hddLog(LOGE, FL("pmksa is NULL"));
return -EINVAL;
}
if (!pmksa->bssid) {
hddLog(LOGE, FL("pmksa->bssid is NULL"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_DEBUG, "%s: deleting PMKSA for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(pmksa->bssid));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
/*Retrieve halHandle*/
halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DEL_PMKSA,
pAdapter->sessionId, 0));
/* Delete the PMKID CSR cache */
if (eHAL_STATUS_SUCCESS !=
sme_RoamDelPMKIDfromCache(halHandle,
pAdapter->sessionId, pmksa->bssid, FALSE)) {
hddLog(LOGE, FL("Failed to delete PMKSA for "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(pmksa->bssid));
status = -EINVAL;
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_pmksa *pmksa)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_del_pmksa(wiphy, dev, pmksa);
vos_ssr_unprotect(__func__);
return ret;
}
static int __wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle halHandle;
hdd_context_t *pHddCtx;
int status = 0;
ENTER();
/* Validate pAdapter */
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Adapter" ,__func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
/*Retrieve halHandle*/
halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
/* Flush the PMKID cache in CSR */
if (eHAL_STATUS_SUCCESS !=
sme_RoamDelPMKIDfromCache(halHandle, pAdapter->sessionId, NULL, TRUE)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Cannot flush PMKIDCache"));
status = -EINVAL;
}
EXIT();
return status;
}
static int wlan_hdd_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_flush_pmksa(wiphy, dev);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
#if defined(WLAN_FEATURE_VOWIFI_11R) && defined(KERNEL_SUPPORT_11R_CFG80211)
static int __wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_station_ctx_t *pHddStaCtx;
hdd_context_t *pHddCtx;
int ret = 0;
ENTER();
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Adapter is NULL", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL == pHddStaCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: STA Context is NULL", __func__);
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_UPDATE_FT_IES,
pAdapter->sessionId, pHddStaCtx->conn_info.connState));
// Added for debug on reception of Re-assoc Req.
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
hddLog(LOGE, FL("Called with Ie of length = %zu when not associated"),
ftie->ie_len);
hddLog(LOGE, FL("Should be Re-assoc Req IEs"));
}
#ifdef WLAN_FEATURE_VOWIFI_11R_DEBUG
hddLog(LOG1, FL("%s called with Ie of length = %zu"), __func__,
ftie->ie_len);
#endif
// Pass the received FT IEs to SME
sme_SetFTIEs( WLAN_HDD_GET_HAL_CTX(pAdapter), pAdapter->sessionId,
(const u8 *)ftie->ie,
ftie->ie_len);
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_update_ft_ies(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_update_ft_ies(wiphy, dev, ftie);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
#ifdef FEATURE_WLAN_SCAN_PNO
void hdd_cfg80211_sched_scan_done_callback(void *callbackContext,
tSirPrefNetworkFoundInd *pPrefNetworkFoundInd)
{
int ret;
hdd_adapter_t* pAdapter = (hdd_adapter_t*)callbackContext;
hdd_context_t *pHddCtx;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return ;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is Null!!!", __func__);
return ;
}
spin_lock(&pHddCtx->schedScan_lock);
if (TRUE == pHddCtx->isWiphySuspended)
{
pHddCtx->isSchedScanUpdatePending = TRUE;
spin_unlock(&pHddCtx->schedScan_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Update cfg80211 scan database after it resume", __func__);
return ;
}
spin_unlock(&pHddCtx->schedScan_lock);
ret = wlan_hdd_cfg80211_update_bss(pHddCtx->wiphy, pAdapter);
if (0 > ret)
hddLog(VOS_TRACE_LEVEL_INFO, "%s: NO SCAN result", __func__);
else
{
/* Acquire wakelock to handle the case where APP's tries to suspend
* immediatly after the driver gets connect request(i.e after pno)
* from supplicant, this result in app's is suspending and not able
* to process the connect request to AP */
hdd_prevent_suspend_timeout(1000, WIFI_POWER_EVENT_WAKELOCK_SCAN);
}
cfg80211_sched_scan_results(pHddCtx->wiphy);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: cfg80211 scan result database updated", __func__);
}
/*
* FUNCTION: wlan_hdd_is_pno_allowed
* Disallow pno if any session is active
*/
static eHalStatus wlan_hdd_is_pno_allowed(hdd_adapter_t *pAdapter)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pTempAdapter = NULL;
hdd_station_ctx_t *pStaCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: PNO is allowed only in STA interface", __func__);
return eHAL_STATUS_FAILURE;
}
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
/* The current firmware design does not allow PNO during any
* active sessions. PNO is allowed only in case when sap session
* is present and sapo auth offload feature enabled in firmare.
*/
while ((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status))
{
pTempAdapter = pAdapterNode->pAdapter;
pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pTempAdapter);
if (((WLAN_HDD_INFRA_STATION == pTempAdapter->device_mode)
&& (eConnectionState_NotConnected != pStaCtx->conn_info.connState))
|| (WLAN_HDD_P2P_CLIENT == pTempAdapter->device_mode)
|| (WLAN_HDD_P2P_GO == pTempAdapter->device_mode)
|| (WLAN_HDD_SOFTAP == pTempAdapter->device_mode &&
!pHddCtx->cfg_ini->enable_sap_auth_offload)
|| (WLAN_HDD_TM_LEVEL_4 == pHddCtx->tmInfo.currentTmLevel)
)
{
return eHAL_STATUS_FAILURE;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return eHAL_STATUS_SUCCESS;
}
void hdd_cfg80211_sched_scan_start_status_cb(void *callbackContext, VOS_STATUS status)
{
hdd_adapter_t *pAdapter = callbackContext;
hdd_context_t *pHddCtx;
ENTER();
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid adapter or adapter has invalid magic"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
{
return;
}
if (VOS_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("PNO enable response status = %d"), status);
pHddCtx->isPnoEnable = FALSE;
}
pAdapter->pno_req_status = (status == VOS_STATUS_SUCCESS) ? 0 : -EBUSY;
complete(&pAdapter->pno_comp_var);
EXIT();
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)) || \
defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT)
/**
* hdd_config_sched_scan_plan() - configures the sched scan plans
* from the framework.
* @pno_req: pointer to PNO scan request
* @request: pointer to scan request from framework
*
* Return: None
*/
static void hdd_config_sched_scan_plan(tpSirPNOScanReq pno_req,
struct cfg80211_sched_scan_request *request,
hdd_context_t *hdd_ctx)
{
v_U32_t i = 0;
pno_req->scanTimers.ucScanTimersCount = request->n_scan_plans;
for (i = 0; i < request->n_scan_plans; i++)
{
pno_req->scanTimers.aTimerValues[i].uTimerRepeat =
request->scan_plans[i].iterations;
pno_req->scanTimers.aTimerValues[i].uTimerValue =
request->scan_plans[i].interval;
}
}
#else
static void hdd_config_sched_scan_plan(tpSirPNOScanReq pno_req,
struct cfg80211_sched_scan_request *request,
hdd_context_t *hdd_ctx)
{
v_U32_t i, temp_int;
/* Driver gets only one time interval which is hardcoded in
* supplicant for 10000ms. Taking power consumption into account 6
* timers will be used, Timervalue is increased exponentially
* i.e 10,20,40, 80,160,320 secs. And number of scan cycle for each
* timer is configurable through INI param gPNOScanTimerRepeatValue.
* If it is set to 0 only one timer will be used and PNO scan cycle
* will be repeated after each interval specified by supplicant
* till PNO is disabled.
*/
if (0 == hdd_ctx->cfg_ini->configPNOScanTimerRepeatValue)
pno_req->scanTimers.ucScanTimersCount =
HDD_PNO_SCAN_TIMERS_SET_ONE;
else
pno_req->scanTimers.ucScanTimersCount =
HDD_PNO_SCAN_TIMERS_SET_MULTIPLE;
temp_int = (request->interval)/1000;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Base scan interval = %d PNOScanTimerRepeatValue = %d",
temp_int, hdd_ctx->cfg_ini->configPNOScanTimerRepeatValue);
for ( i = 0; i < pno_req->scanTimers.ucScanTimersCount; i++)
{
pno_req->scanTimers.aTimerValues[i].uTimerRepeat =
hdd_ctx->cfg_ini->configPNOScanTimerRepeatValue;
pno_req->scanTimers.aTimerValues[i].uTimerValue = temp_int;
temp_int *= 2;
}
//Repeat last timer until pno disabled.
pno_req->scanTimers.aTimerValues[i-1].uTimerRepeat = 0;
}
#endif
/*
* FUNCTION: __wlan_hdd_cfg80211_sched_scan_start
* Function to enable PNO
*/
static int __wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev, struct cfg80211_sched_scan_request *request)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tSirPNOScanReq pnoRequest = {0};
hdd_context_t *pHddCtx;
tHalHandle hHal;
v_U32_t i, indx, num_ch, j;
u8 valid_ch[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
u8 channels_allowed[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
v_U32_t num_channels_allowed = WNI_CFG_VALID_CHANNEL_LIST_LEN;
eHalStatus status = eHAL_STATUS_FAILURE;
int ret = 0;
hdd_config_t *pConfig = NULL;
v_U32_t num_ignore_dfs_ch = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return -EINVAL;
}
pConfig = pHddCtx->cfg_ini;
hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
if (NULL == hHal)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HAL context is Null!!!", __func__);
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SCHED_SCAN_START,
pAdapter->sessionId, pAdapter->device_mode));
ret = wlan_hdd_scan_abort(pAdapter);
if (ret < 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: aborting the existing scan is unsuccessfull", __func__);
return -EBUSY;
}
if (eHAL_STATUS_SUCCESS != wlan_hdd_is_pno_allowed(pAdapter))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("Cannot handle sched_scan"));
return -EBUSY;
}
if (TRUE == pHddCtx->isPnoEnable)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("already PNO is enabled"));
return -EBUSY;
}
if (VOS_STATUS_SUCCESS != wlan_hdd_cancel_remain_on_channel(pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: abort ROC failed ", __func__);
return -EBUSY;
}
pHddCtx->isPnoEnable = TRUE;
pnoRequest.enable = 1; /*Enable PNO */
pnoRequest.ucNetworksCount = request->n_match_sets;
if (( !pnoRequest.ucNetworksCount ) ||
( pnoRequest.ucNetworksCount > SIR_PNO_MAX_SUPP_NETWORKS ))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Network input is not correct %d Max Network supported is %d",
__func__, pnoRequest.ucNetworksCount,
SIR_PNO_MAX_SUPP_NETWORKS);
ret = -EINVAL;
goto error;
}
if ( SIR_PNO_MAX_NETW_CHANNELS_EX < request->n_channels )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Incorrect number of channels %d",
__func__, request->n_channels);
ret = -EINVAL;
goto error;
}
/* Framework provides one set of channels(all)
* common for all saved profile */
if (0 != ccmCfgGetStr(hHal, WNI_CFG_VALID_CHANNEL_LIST,
channels_allowed, &num_channels_allowed))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to get valid channel list", __func__);
ret = -EINVAL;
goto error;
}
/* Checking each channel against allowed channel list */
num_ch = 0;
if (request->n_channels)
{
char chList [(request->n_channels*5)+1];
int len;
for (i = 0, len = 0; i < request->n_channels; i++)
{
for (indx = 0; indx < num_channels_allowed; indx++)
{
if (request->channels[i]->hw_value == channels_allowed[indx])
{
if ((!pConfig->enableDFSPnoChnlScan) &&
(NV_CHANNEL_DFS == vos_nv_getChannelEnabledState(channels_allowed[indx])))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : Dropping DFS channel : %d",
__func__,channels_allowed[indx]);
num_ignore_dfs_ch++;
break;
}
valid_ch[num_ch++] = request->channels[i]->hw_value;
len += snprintf(chList+len, 5, "%d ",
request->channels[i]->hw_value);
break ;
}
}
}
hddLog(VOS_TRACE_LEVEL_INFO,"Channel-List: %s ", chList);
/*If all channels are DFS and dropped, then ignore the PNO request*/
if (num_ignore_dfs_ch == request->n_channels)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : All requested channels are DFS channels", __func__);
ret = -EINVAL;
goto error;
}
}
pnoRequest.aNetworks =
vos_mem_malloc(sizeof(tSirNetworkType)*pnoRequest.ucNetworksCount);
if (pnoRequest.aNetworks == NULL)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("failed to allocate memory aNetworks %u"),
(uint32)sizeof(tSirNetworkType)*pnoRequest.ucNetworksCount);
goto error;
}
vos_mem_zero(pnoRequest.aNetworks,
sizeof(tSirNetworkType)*pnoRequest.ucNetworksCount);
/* Filling per profile params */
for (i = 0; i < pnoRequest.ucNetworksCount; i++)
{
pnoRequest.aNetworks[i].ssId.length =
request->match_sets[i].ssid.ssid_len;
if (( 0 == pnoRequest.aNetworks[i].ssId.length ) ||
( pnoRequest.aNetworks[i].ssId.length > 32 ) )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: SSID Len %d is not correct for network %d",
__func__, pnoRequest.aNetworks[i].ssId.length, i);
ret = -EINVAL;
goto error;
}
memcpy(pnoRequest.aNetworks[i].ssId.ssId,
request->match_sets[i].ssid.ssid,
request->match_sets[i].ssid.ssid_len);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: SSID of network %d is %s ", __func__,
i, pnoRequest.aNetworks[i].ssId.ssId);
pnoRequest.aNetworks[i].authentication = 0; /*eAUTH_TYPE_ANY*/
pnoRequest.aNetworks[i].encryption = 0; /*eED_ANY*/
pnoRequest.aNetworks[i].bcastNetwType = 0; /*eBCAST_UNKNOWN*/
/*Copying list of valid channel into request */
memcpy(pnoRequest.aNetworks[i].aChannels, valid_ch, num_ch);
pnoRequest.aNetworks[i].ucChannelCount = num_ch;
pnoRequest.aNetworks[i].rssiThreshold = 0; //Default value
}
for (i = 0; i < request->n_ssids; i++)
{
j = 0;
while (j < pnoRequest.ucNetworksCount)
{
if ((pnoRequest.aNetworks[j].ssId.length ==
request->ssids[i].ssid_len) &&
(0 == memcmp(pnoRequest.aNetworks[j].ssId.ssId,
request->ssids[i].ssid,
pnoRequest.aNetworks[j].ssId.length)))
{
pnoRequest.aNetworks[j].bcastNetwType = eBCAST_HIDDEN;
break;
}
j++;
}
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Number of hidden networks being Configured = %d",
request->n_ssids);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"request->ie_len = %zu", request->ie_len);
pnoRequest.p24GProbeTemplate = vos_mem_malloc(SIR_PNO_MAX_PB_REQ_SIZE);
if (pnoRequest.p24GProbeTemplate == NULL)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("failed to allocate memory p24GProbeTemplate %u"),
SIR_PNO_MAX_PB_REQ_SIZE);
goto error;
}
pnoRequest.p5GProbeTemplate = vos_mem_malloc(SIR_PNO_MAX_PB_REQ_SIZE);
if (pnoRequest.p5GProbeTemplate == NULL)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
FL("failed to allocate memory p5GProbeTemplate %u"),
SIR_PNO_MAX_PB_REQ_SIZE);
goto error;
}
vos_mem_zero(pnoRequest.p24GProbeTemplate, SIR_PNO_MAX_PB_REQ_SIZE);
vos_mem_zero(pnoRequest.p5GProbeTemplate, SIR_PNO_MAX_PB_REQ_SIZE);
if ((0 < request->ie_len) && (request->ie_len <= SIR_PNO_MAX_PB_REQ_SIZE) &&
(NULL != request->ie))
{
pnoRequest.us24GProbeTemplateLen = request->ie_len;
memcpy(pnoRequest.p24GProbeTemplate, request->ie,
pnoRequest.us24GProbeTemplateLen);
pnoRequest.us5GProbeTemplateLen = request->ie_len;
memcpy(pnoRequest.p5GProbeTemplate, request->ie,
pnoRequest.us5GProbeTemplateLen);
}
hdd_config_sched_scan_plan(&pnoRequest, request, pHddCtx);
pnoRequest.modePNO = SIR_PNO_MODE_IMMEDIATE;
INIT_COMPLETION(pAdapter->pno_comp_var);
pnoRequest.statusCallback = hdd_cfg80211_sched_scan_start_status_cb;
pnoRequest.callbackContext = pAdapter;
pAdapter->pno_req_status = 0;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"SessionId %d, enable %d, modePNO %d, ucScanTimersCount %d",
pAdapter->sessionId, pnoRequest.enable, pnoRequest.modePNO,
pnoRequest.scanTimers.ucScanTimersCount);
status = sme_SetPreferredNetworkList(WLAN_HDD_GET_HAL_CTX(pAdapter),
&pnoRequest, pAdapter->sessionId,
hdd_cfg80211_sched_scan_done_callback, pAdapter);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to enable PNO", __func__);
ret = -EINVAL;
goto error;
}
ret = wait_for_completion_timeout(
&pAdapter->pno_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_PNO));
if (0 >= ret)
{
// Did not receive the response for PNO enable in time.
// Assuming the PNO enable was success.
// Returning error from here, because we timeout, results
// in side effect of Wifi (Wifi Setting) not to work.
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Timed out waiting for PNO to be Enabled"));
ret = 0;
}
ret = pAdapter->pno_req_status;
return ret;
error:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("PNO scanRequest offloaded ret = %d"), ret);
pHddCtx->isPnoEnable = FALSE;
if (pnoRequest.aNetworks)
vos_mem_free(pnoRequest.aNetworks);
if (pnoRequest.p24GProbeTemplate)
vos_mem_free(pnoRequest.p24GProbeTemplate);
if (pnoRequest.p5GProbeTemplate)
vos_mem_free(pnoRequest.p5GProbeTemplate);
EXIT();
return ret;
}
/*
* FUNCTION: wlan_hdd_cfg80211_sched_scan_start
* NL interface to enable PNO
*/
static int wlan_hdd_cfg80211_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev, struct cfg80211_sched_scan_request *request)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_sched_scan_start(wiphy, dev, request);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_sched_scan_stop
* Function to disable PNO
*/
static int __wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy,
struct net_device *dev)
{
eHalStatus status = eHAL_STATUS_FAILURE;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
tHalHandle hHal;
tSirPNOScanReq pnoRequest = {0};
int ret = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is Null", __func__);
return -ENODEV;
}
/* The return 0 is intentional when isLogpInProgress and
* isLoadUnloadInProgress. We did observe a crash due to a return of
* failure in sched_scan_stop , especially for a case where the unload
* of the happens at the same time. The function __cfg80211_stop_sched_scan
* was clearing rdev->sched_scan_req only when the sched_scan_stop returns
* success. If it returns a failure , then its next invocation due to the
* clean up of the second interface will have the dev pointer corresponding
* to the first one leading to a crash.
*/
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: LOGP in Progress. Ignore!!!", __func__);
pHddCtx->isPnoEnable = FALSE;
return ret;
}
if (WLAN_HDD_IS_LOAD_UNLOAD_IN_PROGRESS(pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Unloading/Loading in Progress. Ignore!!!", __func__);
return ret;
}
hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
if (NULL == hHal)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HAL context is Null!!!", __func__);
return -EINVAL;
}
pnoRequest.enable = 0; /* Disable PNO */
pnoRequest.ucNetworksCount = 0;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SCHED_SCAN_STOP,
pAdapter->sessionId, pAdapter->device_mode));
INIT_COMPLETION(pAdapter->pno_comp_var);
pnoRequest.statusCallback = hdd_cfg80211_sched_scan_start_status_cb;
pnoRequest.callbackContext = pAdapter;
pAdapter->pno_req_status = 0;
status = sme_SetPreferredNetworkList(hHal, &pnoRequest,
pAdapter->sessionId,
NULL, pAdapter);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Failed to disabled PNO");
ret = -EINVAL;
goto error;
}
ret = wait_for_completion_timeout(
&pAdapter->pno_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_PNO));
if (0 >= ret)
{
// Did not receive the response for PNO disable in time.
// Assuming the PNO disable was success.
// Returning error from here, because we timeout, results
// in side effect of Wifi (Wifi Setting) not to work.
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("Timed out waiting for PNO to be disabled"));
ret = 0;
}
ret = pAdapter->pno_req_status;
pHddCtx->isPnoEnable = (ret == 0) ? FALSE : TRUE;
error:
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("PNO scan disabled ret = %d"), ret);
EXIT();
return ret;
}
/*
* FUNCTION: wlan_hdd_cfg80211_sched_scan_stop
* NL interface to disable PNO
*/
static int wlan_hdd_cfg80211_sched_scan_stop(struct wiphy *wiphy,
struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_sched_scan_stop(wiphy, dev);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /*FEATURE_WLAN_SCAN_PNO*/
#ifdef FEATURE_WLAN_TDLS
#if TDLS_MGMT_VERSION2
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else /* TDLS_MGMT_VERSION2 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || \
defined(WITH_BACKPORTS)
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, bool initiator,
const u8 *buf, size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, const u8 *buf,
size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else
static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, const u8 *buf,
size_t len)
#endif
#endif
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
VOS_STATUS status;
int max_sta_failed = 0;
int responder;
long rc;
int ret;
hddTdlsPeer_t *pTdlsPeer;
#if !(TDLS_MGMT_VERSION2) && (LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0))
u32 peer_capability = 0;
#endif
tANI_U16 numCurrTdlsPeers;
hdd_station_ctx_t *pHddStaCtx = NULL;
tdlsCtx_t *pHddTdlsCtx;
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Adapter is NULL",__func__);
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_TDLS_MGMT,
pAdapter->sessionId, action_code));
pHddCtx = wiphy_priv(wiphy);
if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid arguments");
return -EINVAL;
}
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:LOGP in Progress. Ignore!!!", __func__);
wlan_hdd_tdls_set_link_status(pAdapter,
peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
return -EBUSY;
}
if (WLAN_HDD_IS_LOAD_UNLOAD_IN_PROGRESS(pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Unloading/Loading in Progress. Ignore!!!", __func__);
return -EAGAIN;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (!pHddTdlsCtx) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pHddTdlsCtx not valid.", __func__);
return -EINVAL;
}
if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS mode is disabled OR not enabled in FW."
MAC_ADDRESS_STR " action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -ENOTSUPP;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if( NULL == pHddStaCtx )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD station context NULL ",__func__);
return -EINVAL;
}
/* STA should be connected and authenticated
* before sending any TDLS frames
*/
if ((eConnectionState_Associated != pHddStaCtx->conn_info.connState) ||
(FALSE == pHddStaCtx->conn_info.uIsAuthenticated))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"STA is not connected or unauthenticated. "
"connState %u, uIsAuthenticated %u",
pHddStaCtx->conn_info.connState,
pHddStaCtx->conn_info.uIsAuthenticated);
return -EAGAIN;
}
/* other than teardown frame, other mgmt frames are not sent if disabled */
if (SIR_MAC_TDLS_TEARDOWN != action_code)
{
/* if tdls_mode is disabled to respond to peer's request */
if (eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR
" TDLS mode is disabled. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -ENOTSUPP;
}
if (vos_max_concurrent_connections_reached())
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Reached max concurrent connections"));
return -EINVAL;
}
}
if (WLAN_IS_TDLS_SETUP_ACTION(action_code))
{
if (NULL != wlan_hdd_tdls_is_progress(pHddCtx, peer, TRUE, TRUE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS setup is ongoing. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -EPERM;
}
}
if (SIR_MAC_TDLS_SETUP_REQ == action_code ||
SIR_MAC_TDLS_SETUP_RSP == action_code )
{
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (HDD_MAX_NUM_TDLS_STA <= numCurrTdlsPeers)
{
/* supplicant still sends tdls_mgmt(SETUP_REQ) even after
we return error code at 'add_station()'. Hence we have this
check again in addtion to add_station().
Anyway, there is no hard to double-check. */
if (SIR_MAC_TDLS_SETUP_REQ == action_code)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected. action (%d) declined. Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(peer), action_code,
numCurrTdlsPeers, HDD_MAX_NUM_TDLS_STA);
return -EINVAL;
}
else
{
/* maximum reached. tweak to send error code to peer and return
error code to supplicant */
status_code = eSIR_MAC_UNSPEC_FAILURE_STATUS;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" TDLS Max peer already connected, send response status (%d). Num of peers (%d), Max allowed (%d).",
__func__, MAC_ADDR_ARRAY(peer), status_code,
numCurrTdlsPeers, HDD_MAX_NUM_TDLS_STA);
max_sta_failed = -EPERM;
/* fall through to send setup resp with failure status
code */
}
}
else
{
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if (pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer))
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:" MAC_ADDRESS_STR " already connected. action %d declined.",
__func__, MAC_ADDR_ARRAY(peer), action_code);
return -EPERM;
}
mutex_unlock(&pHddCtx->tdls_lock);
}
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " action %d, dialog_token %d status %d, len = %zu",
"tdls_mgmt", MAC_ADDR_ARRAY(peer),
action_code, dialog_token, status_code, len);
/*Except teardown responder will not be used so just make 0*/
responder = 0;
if (SIR_MAC_TDLS_TEARDOWN == action_code)
{
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if(pTdlsPeer && TDLS_IS_CONNECTED(pTdlsPeer))
responder = pTdlsPeer->is_responder;
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " peer doesn't exist or not connected %d dialog_token %d status %d, len = %zu",
__func__, MAC_ADDR_ARRAY(peer), (NULL == pTdlsPeer) ? -1 : pTdlsPeer->link_status,
dialog_token, status_code, len);
mutex_unlock(&pHddCtx->tdls_lock);
return -EPERM;
}
mutex_unlock(&pHddCtx->tdls_lock);
}
/* Discard TDLS setup if peer is removed by user app */
if ((pHddCtx->cfg_ini->fTDLSExternalControl) &&
((SIR_MAC_TDLS_SETUP_REQ == action_code) ||
(SIR_MAC_TDLS_SETUP_CNF == action_code) ||
(SIR_MAC_TDLS_DIS_REQ == action_code))) {
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if (pTdlsPeer && (FALSE == pTdlsPeer->isForcedPeer)) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOGE, FL("TDLS External Control enabled, but peer "
MAC_ADDRESS_STR " is not forced, so reject the action code %d"),
MAC_ADDR_ARRAY(peer), action_code);
return -EINVAL;
}
mutex_unlock(&pHddCtx->tdls_lock);
}
/* For explicit trigger of DIS_REQ come out of BMPS for
successfully receiving DIS_RSP from peer. */
if ((SIR_MAC_TDLS_SETUP_RSP == action_code) ||
(SIR_MAC_TDLS_SETUP_CNF== action_code) ||
(SIR_MAC_TDLS_DIS_RSP == action_code) ||
(SIR_MAC_TDLS_DIS_REQ == action_code))
{
if (TRUE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter))) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Sending frame action_code %u.Disable BMPS", __func__,
action_code);
status = hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_INFRA_STATION);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set BMPS/IMPS"));
} else {
pHddTdlsCtx->is_tdls_disabled_bmps = true;
}
}
if (SIR_MAC_TDLS_DIS_REQ != action_code) {
if (0 != wlan_hdd_tdls_set_cap(pAdapter, peer, eTDLS_CAP_SUPPORTED)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set TDLS capabilities"));
}
}
}
/* make sure doesn't call send_mgmt() while it is pending */
if (TDLS_CTX_MAGIC == pAdapter->mgmtTxCompletionStatus)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " action %d couldn't sent, as one is pending. return EBUSY",
__func__, MAC_ADDR_ARRAY(peer), action_code);
ret = -EBUSY;
goto tx_failed;
}
pAdapter->mgmtTxCompletionStatus = TDLS_CTX_MAGIC;
INIT_COMPLETION(pAdapter->tdls_mgmt_comp);
status = sme_SendTdlsMgmtFrame(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, peer, action_code, dialog_token,
status_code, peer_capability, (tANI_U8 *)buf, len,
responder);
if (VOS_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_SendTdlsMgmtFrame failed!", __func__);
pAdapter->mgmtTxCompletionStatus = FALSE;
ret = -EINVAL;
goto tx_failed;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Wait for tdls_mgmt_comp. Timeout %u ms", __func__,
WAIT_TIME_TDLS_MGMT);
rc = wait_for_completion_interruptible_timeout(&pAdapter->tdls_mgmt_comp,
msecs_to_jiffies(WAIT_TIME_TDLS_MGMT));
if ((rc <= 0) || (TRUE != pAdapter->mgmtTxCompletionStatus))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Mgmt Tx Completion failed status %ld TxCompletion %u",
__func__, rc, pAdapter->mgmtTxCompletionStatus);
pAdapter->mgmtTxCompletionStatus = FALSE;
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: LOGP in Progress. Ignore!!!", __func__);
return -EAGAIN;
}
if (rc <= 0)
vos_fatal_event_logs_req(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_HDD_TIME_OUT,
TRUE, TRUE);
ret = -EINVAL;
goto tx_failed;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Mgmt Tx Completion status %ld TxCompletion %u",
__func__, rc, pAdapter->mgmtTxCompletionStatus);
}
if (max_sta_failed)
{
ret = max_sta_failed;
goto tx_failed;
}
if (SIR_MAC_TDLS_SETUP_RSP == action_code)
{
if (0 != wlan_hdd_tdls_set_responder(pAdapter, peer, TRUE)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set TDLS responder: Setup Response"));
}
}
else if (SIR_MAC_TDLS_SETUP_CNF == action_code)
{
if (0 != wlan_hdd_tdls_set_responder(pAdapter, peer, FALSE)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set TDLS responder: Setup Response"));
}
}
return 0;
tx_failed:
/* add_station will be called before sending TDLS_SETUP_REQ and
* TDLS_SETUP_RSP and as part of add_station driver will enable
* BMPS. NL80211_TDLS_DISABLE_LINK will be called if the tx of
* TDLS_SETUP_REQ or TDLS_SETUP_RSP fails. BMPS will be enabled
* as part of processing NL80211_TDLS_DISABLE_LINK. So need to
* enable BMPS for TDLS_SETUP_REQ and TDLS_SETUP_RSP if tx fails.
*/
if ((SIR_MAC_TDLS_SETUP_REQ == action_code) ||
(SIR_MAC_TDLS_SETUP_RSP == action_code))
wlan_hdd_tdls_check_bmps(pAdapter);
return ret;
}
#if TDLS_MGMT_VERSION2
static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else /* TDLS_MGMT_VERSION2 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0))
static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, bool initiator,
const u8 *buf, size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
const u8 *peer, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capability, const u8 *buf,
size_t len)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
struct net_device *dev,
u8 *peer, u8 action_code,
u8 dialog_token,
u16 status_code, u32 peer_capability,
const u8 *buf, size_t len)
#else
static int wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, const u8 *buf, size_t len)
#endif
#endif
{
int ret;
vos_ssr_protect(__func__);
#if TDLS_MGMT_VERSION2
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#else /* TDLS_MGMT_VERSION2 */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || \
defined(WITH_BACKPORTS)
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
buf, len);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, buf, len);
#else
ret = __wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer, action_code,
dialog_token, status_code, buf, len);
#endif
#endif
vos_ssr_unprotect(__func__);
return ret;
}
int wlan_hdd_tdls_extctrl_config_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *peer,
#else
u8 *peer,
#endif
tdls_req_params_t *tdls_peer_params,
cfg80211_exttdls_callback callback)
{
hddTdlsPeer_t *pTdlsPeer = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s : NL80211_TDLS_SETUP for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
(FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s TDLS External control (%d) and Implicit Trigger (%d) not enabled ",
__func__, pHddCtx->cfg_ini->fTDLSExternalControl,
pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger);
return -ENOTSUPP;
}
/* To cater the requirement of establishing the TDLS link
* irrespective of the data traffic , get an entry of TDLS peer.
*/
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_get_peer(pAdapter, peer);
if (pTdlsPeer == NULL) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: peer " MAC_ADDRESS_STR " not existing",
__func__, MAC_ADDR_ARRAY(peer));
mutex_unlock(&pHddCtx->tdls_lock);
return -EINVAL;
}
/* check FW TDLS Off Channel capability */
if ((TRUE == sme_IsFeatureSupportedByFW(TDLS_OFF_CHANNEL)) &&
(TRUE == pHddCtx->cfg_ini->fEnableTDLSOffChannel) &&
(NULL != tdls_peer_params))
{
pTdlsPeer->peerParams.channel = tdls_peer_params->channel;
pTdlsPeer->peerParams.global_operating_class =
tdls_peer_params->global_operating_class;
pTdlsPeer->peerParams.max_latency_ms = tdls_peer_params->max_latency_ms;
pTdlsPeer->peerParams.min_bandwidth_kbps =
tdls_peer_params->min_bandwidth_kbps;
/* check configured channel is valid, non dfs and
* not current operating channel */
if ((sme_IsTdlsOffChannelValid(WLAN_HDD_GET_HAL_CTX(pAdapter),
tdls_peer_params->channel)) &&
(pHddStaCtx) &&
(tdls_peer_params->channel !=
pHddStaCtx->conn_info.operationChannel))
{
pTdlsPeer->isOffChannelConfigured = TRUE;
}
else
{
pTdlsPeer->isOffChannelConfigured = FALSE;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Configured Tdls Off Channel is not valid", __func__);
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls_off_channel %d isOffChannelConfigured %d "
"current operating channel %d",
__func__, pTdlsPeer->peerParams.channel,
pTdlsPeer->isOffChannelConfigured,
(pHddStaCtx ? pHddStaCtx->conn_info.operationChannel : 0));
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: TDLS off channel FW capability %d, "
"host capab %d or Invalid TDLS Peer Params", __func__,
sme_IsFeatureSupportedByFW(TDLS_OFF_CHANNEL),
pHddCtx->cfg_ini->fEnableTDLSOffChannel);
}
if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, TRUE) ) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
" %s TDLS Add Force Peer Failed",
__func__);
return -EINVAL;
}
/*EXT TDLS*/
if ( 0 != wlan_hdd_set_callback(pTdlsPeer, callback) ) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
" %s TDLS set callback Failed",
__func__);
return -EINVAL;
}
mutex_unlock(&pHddCtx->tdls_lock);
return(0);
}
int wlan_hdd_tdls_extctrl_deconfig_peer(hdd_adapter_t *pAdapter,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *peer
#else
u8 *peer
#endif
)
{
hddTdlsPeer_t *pTdlsPeer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s : NL80211_TDLS_TEARDOWN for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
if (0 != wlan_hdd_validate_context(pHddCtx)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is NULL"));
return -EINVAL;
}
if ( (FALSE == pHddCtx->cfg_ini->fTDLSExternalControl) ||
(FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger) ) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s TDLS External control (%d) and Implicit Trigger (%d) not enabled ",
__func__, pHddCtx->cfg_ini->fTDLSExternalControl,
pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger);
return -ENOTSUPP;
}
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR
" peer not existing",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
else {
wlan_hdd_tdls_indicate_teardown(pAdapter, pTdlsPeer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_EXT_CTRL,
pTdlsPeer->peerMac);
/* if channel switch is configured, reset
the channel for this peer */
if (TRUE == pTdlsPeer->isOffChannelConfigured)
{
pTdlsPeer->peerParams.channel = 0;
pTdlsPeer->isOffChannelConfigured = FALSE;
}
}
if ( 0 != wlan_hdd_tdls_set_force_peer(pAdapter, peer, FALSE) ) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to set force peer"));
return -EINVAL;
}
/*EXT TDLS*/
if ( 0 != wlan_hdd_set_callback(pTdlsPeer, NULL )) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
" %s TDLS set callback Failed",
__func__);
return -EINVAL;
}
mutex_unlock(&pHddCtx->tdls_lock);
return(0);
}
static int __wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *peer,
#else
u8 *peer,
#endif
enum nl80211_tdls_operation oper)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
int status;
hddTdlsPeer_t *pTdlsPeer;
ENTER();
if (!pAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD adpater is NULL"));
return -EINVAL;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_TDLS_OPER,
pAdapter->sessionId, oper));
if ( NULL == peer )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid arguments", __func__);
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if( FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport ||
FALSE == sme_IsFeatureSupportedByFW(TDLS))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"TDLS Disabled in INI (%d) OR not enabled in FW (%d) "
"Cannot process TDLS commands",
pHddCtx->cfg_ini->fEnableTDLSSupport,
sme_IsFeatureSupportedByFW(TDLS));
return -ENOTSUPP;
}
switch (oper) {
case NL80211_TDLS_ENABLE_LINK:
{
VOS_STATUS status;
long ret;
tCsrTdlsLinkEstablishParams tdlsLinkEstablishParams = { {0}, 0,
0, 0, 0, 0, 0, 0, {0}, 0, {0} };
WLAN_STADescType staDesc;
tANI_U16 numCurrTdlsPeers = 0;
hddTdlsPeer_t *connPeer = NULL;
tANI_U8 suppChannelLen = 0;
tSirMacAddr peerMac;
int channel;
tTDLSLinkStatus peer_status = eTDLS_LINK_IDLE;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s : NL80211_TDLS_ENABLE_LINK for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
memset(&staDesc, 0, sizeof(staDesc));
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR
" (oper %d) not exsting. ignored",
__func__, MAC_ADDR_ARRAY(peer), (int)oper);
return -EINVAL;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " link_status %d (%s) ", "tdls_oper",
MAC_ADDR_ARRAY(peer), pTdlsPeer->link_status,
"NL80211_TDLS_ENABLE_LINK");
if (!TDLS_STA_INDEX_VALID(pTdlsPeer->staId))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Staion Index %u "
MAC_ADDRESS_STR " failed",
__func__, pTdlsPeer->staId, MAC_ADDR_ARRAY(peer));
mutex_unlock(&pHddCtx->tdls_lock);
return -EINVAL;
}
/* before starting tdls connection, set tdls
* off channel established status to default value */
pTdlsPeer->isOffChannelEstablished = FALSE;
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_set_cap(pAdapter, peer, eTDLS_CAP_SUPPORTED);
/* TDLS Off Channel, Disable tdls channel switch,
when there are more than one tdls link */
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (numCurrTdlsPeers == 2)
{
mutex_lock(&pHddCtx->tdls_lock);
/* get connected peer and send disable tdls off chan */
connPeer = wlan_hdd_tdls_get_connected_peer(pAdapter);
if ((connPeer) &&
(connPeer->isOffChannelSupported == TRUE) &&
(connPeer->isOffChannelConfigured == TRUE))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: More then one peer connected, Disable "
"TDLS channel switch", __func__);
connPeer->isOffChannelEstablished = FALSE;
vos_mem_copy(peerMac, connPeer->peerMac, sizeof (tSirMacAddr));
channel = connPeer->peerParams.channel;
mutex_unlock(&pHddCtx->tdls_lock);
ret = sme_SendTdlsChanSwitchReq(
WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
peerMac,
channel,
TDLS_OFF_CHANNEL_BW_OFFSET,
TDLS_CHANNEL_SWITCH_DISABLE);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to send TDLS switch channel request"));
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: No TDLS Connected Peer or "
"isOffChannelSupported %d "
"isOffChannelConfigured %d",
__func__,
(connPeer ? (connPeer->isOffChannelSupported)
: -1),
(connPeer ? (connPeer->isOffChannelConfigured)
: -1));
mutex_unlock(&pHddCtx->tdls_lock);
}
}
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" (oper %d) peer got freed in other context. ignored",
__func__, MAC_ADDR_ARRAY(peer), (int)oper);
return -EINVAL;
}
peer_status = pTdlsPeer->link_status;
mutex_unlock(&pHddCtx->tdls_lock);
if (eTDLS_LINK_CONNECTED != peer_status)
{
if (IS_ADVANCE_TDLS_ENABLE) {
if (0 != wlan_hdd_tdls_get_link_establish_params(
pAdapter, peer,&tdlsLinkEstablishParams)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to get link establishment params"));
return -EINVAL;
}
INIT_COMPLETION(pAdapter->tdls_link_establish_req_comp);
ret = sme_SendTdlsLinkEstablishParams(
WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, peer,
&tdlsLinkEstablishParams);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to send link establishment params"));
}
/* Send TDLS peer UAPSD capabilities to the firmware and
* register with the TL on after the response for this operation
* is received .
*/
ret = wait_for_completion_interruptible_timeout(
&pAdapter->tdls_link_establish_req_comp,
msecs_to_jiffies(WAIT_TIME_TDLS_LINK_ESTABLISH_REQ));
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s %d: " MAC_ADDRESS_STR
" (oper %d) peer got freed in other context. ignored",
__func__, __LINE__, MAC_ADDR_ARRAY(peer),
(int)oper);
return -EINVAL;
}
peer_status = pTdlsPeer->link_status;
mutex_unlock(&pHddCtx->tdls_lock);
if (ret <= 0 || (peer_status == eTDLS_LINK_TEARING))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Link Establish Request Failed Status %ld"),
ret);
return -EINVAL;
}
}
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR
" (oper %d) peer got freed in other context. ignored",
__func__, MAC_ADDR_ARRAY(peer), (int)oper);
return -EINVAL;
}
wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
eTDLS_LINK_CONNECTED,
eTDLS_LINK_SUCCESS);
staDesc.ucSTAId = pTdlsPeer->staId;
staDesc.ucQosEnabled = tdlsLinkEstablishParams.qos;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdlsLinkEstablishParams of peer "
MAC_ADDRESS_STR "uapsdQueues: %d"
"qos: %d maxSp: %d isBufSta: %d isOffChannelSupported: %d"
"isResponder: %d peerstaId: %d",
__func__,
MAC_ADDR_ARRAY(tdlsLinkEstablishParams.peerMac),
tdlsLinkEstablishParams.uapsdQueues,
tdlsLinkEstablishParams.qos,
tdlsLinkEstablishParams.maxSp,
tdlsLinkEstablishParams.isBufSta,
tdlsLinkEstablishParams.isOffChannelSupported,
tdlsLinkEstablishParams.isResponder,
pTdlsPeer->staId);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: StaDesc ucSTAId: %d ucQosEnabled: %d",
__func__,
staDesc.ucSTAId,
staDesc.ucQosEnabled);
ret = WLANTL_UpdateTdlsSTAClient(
pHddCtx->pvosContext,
&staDesc);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to update TDLS STA params"));
}
/* Mark TDLS client Authenticated .*/
status = WLANTL_ChangeSTAState( pHddCtx->pvosContext,
pTdlsPeer->staId,
WLANTL_STA_AUTHENTICATED);
if (VOS_STATUS_SUCCESS == status)
{
if (pTdlsPeer->is_responder == 0)
{
v_U8_t staId = (v_U8_t)pTdlsPeer->staId;
tdlsConnInfo_t *tdlsInfo;
tdlsInfo = wlan_hdd_get_conn_info(pHddCtx, staId);
if (!vos_timer_is_initialized(
&pTdlsPeer->initiatorWaitTimeoutTimer))
{
/* Initialize initiator wait callback */
vos_timer_init(
&pTdlsPeer->initiatorWaitTimeoutTimer,
VOS_TIMER_TYPE_SW,
wlan_hdd_tdls_initiator_wait_cb,
tdlsInfo);
}
wlan_hdd_tdls_timer_restart(pAdapter,
&pTdlsPeer->initiatorWaitTimeoutTimer,
WAIT_TIME_TDLS_INITIATOR);
/* suspend initiator TX until it receives direct packet from the
reponder or WAIT_TIME_TDLS_INITIATOR timer expires */
ret = WLANTL_SuspendDataTx(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
&staId, NULL);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to suspend data tx"));
}
}
if ((TRUE == pTdlsPeer->isOffChannelSupported) &&
(TRUE == pTdlsPeer->isOffChannelConfigured))
{
suppChannelLen =
tdlsLinkEstablishParams.supportedChannelsLen;
if ((suppChannelLen > 0) &&
(suppChannelLen <= SIR_MAC_MAX_SUPP_CHANNELS))
{
tANI_U8 suppPeerChannel = 0;
int i = 0;
for (i = 0U; i < suppChannelLen; i++)
{
suppPeerChannel =
tdlsLinkEstablishParams.supportedChannels[i];
pTdlsPeer->isOffChannelSupported = FALSE;
if (suppPeerChannel ==
pTdlsPeer->peerParams.channel)
{
pTdlsPeer->isOffChannelSupported = TRUE;
break;
}
}
}
else
{
pTdlsPeer->isOffChannelSupported = FALSE;
}
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS channel switch request for channel "
"%d isOffChannelConfigured %d suppChannelLen "
"%d isOffChannelSupported %d", __func__,
pTdlsPeer->peerParams.channel,
pTdlsPeer->isOffChannelConfigured,
suppChannelLen,
pTdlsPeer->isOffChannelSupported);
/* TDLS Off Channel, Enable tdls channel switch,
when their is only one tdls link and it supports */
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if ((numCurrTdlsPeers == 1) &&
(TRUE == pTdlsPeer->isOffChannelSupported) &&
(TRUE == pTdlsPeer->isOffChannelConfigured))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Send TDLS channel switch request for channel %d",
__func__, pTdlsPeer->peerParams.channel);
pTdlsPeer->isOffChannelEstablished = TRUE;
vos_mem_copy(peerMac, pTdlsPeer->peerMac, sizeof (tSirMacAddr));
channel = pTdlsPeer->peerParams.channel;
mutex_unlock(&pHddCtx->tdls_lock);
ret = sme_SendTdlsChanSwitchReq(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
peerMac,
channel,
TDLS_OFF_CHANNEL_BW_OFFSET,
TDLS_CHANNEL_SWITCH_ENABLE);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS offchannel: Failed to send TDLS switch channel req"));
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: TDLS channel switch request not sent"
" numCurrTdlsPeers %d "
"isOffChannelSupported %d "
"isOffChannelConfigured %d",
__func__, numCurrTdlsPeers,
pTdlsPeer->isOffChannelSupported,
pTdlsPeer->isOffChannelConfigured);
mutex_unlock(&pHddCtx->tdls_lock);
}
}
else
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pAdapter);
/* Update TL about the UAPSD masks , to route the packets to firmware */
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSBufferSta)
|| pHddCtx->cfg_ini->fTDLSUapsdMask )
{
int ac;
uint8 ucAc[4] = { WLANTL_AC_VO,
WLANTL_AC_VI,
WLANTL_AC_BK,
WLANTL_AC_BE };
uint8 tlTid[4] = { 7, 5, 2, 3 } ;
for(ac=0; ac < 4; ac++)
{
status = WLANTL_EnableUAPSDForAC( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
pTdlsPeer->staId, ucAc[ac],
tlTid[ac], tlTid[ac], 0, 0,
WLANTL_BI_DIR );
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to enable UAPSD for AC"));
}
}
}
}
/* stop TCP delack timer if TDLS is enable */
set_bit(WLAN_TDLS_MODE, &pHddCtx->mode);
hdd_manage_delack_timer(pHddCtx);
hdd_wlan_tdls_enable_link_event(peer,
pTdlsPeer->isOffChannelSupported,
pTdlsPeer->isOffChannelConfigured,
pTdlsPeer->isOffChannelEstablished);
}
break;
case NL80211_TDLS_DISABLE_LINK:
{
tANI_U16 numCurrTdlsPeers = 0;
hddTdlsPeer_t *connPeer = NULL;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" %s : NL80211_TDLS_DISABLE_LINK for " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY(peer));
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR
" (oper %d) not exsting. ignored",
__func__, MAC_ADDR_ARRAY(peer), (int)oper);
return -EINVAL;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: " MAC_ADDRESS_STR " link_status %d (%s) ", "tdls_oper",
MAC_ADDR_ARRAY(peer), pTdlsPeer->link_status,
"NL80211_TDLS_DISABLE_LINK");
if(TDLS_STA_INDEX_VALID(pTdlsPeer->staId))
{
long status;
/* set tdls off channel status to false for this peer */
pTdlsPeer->isOffChannelEstablished = FALSE;
wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
eTDLS_LINK_TEARING,
(pTdlsPeer->link_status == eTDLS_LINK_TEARING)?
eTDLS_LINK_UNSPECIFIED:
eTDLS_LINK_DROPPED_BY_REMOTE);
mutex_unlock(&pHddCtx->tdls_lock);
INIT_COMPLETION(pAdapter->tdls_del_station_comp);
status = sme_DeleteTdlsPeerSta(
WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, peer );
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to delete TDLS peer STA"));
}
status = wait_for_completion_interruptible_timeout(&pAdapter->tdls_del_station_comp,
msecs_to_jiffies(WAIT_TIME_TDLS_DEL_STA));
mutex_lock(&pHddCtx->tdls_lock);
pTdlsPeer = wlan_hdd_tdls_find_peer(pAdapter, peer, FALSE);
if ( NULL == pTdlsPeer ) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: " MAC_ADDRESS_STR
" peer was freed in other context",
__func__, MAC_ADDR_ARRAY(peer));
return -EINVAL;
}
wlan_hdd_tdls_set_peer_link_status(pTdlsPeer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED);
mutex_unlock(&pHddCtx->tdls_lock);
if (status <= 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Del station failed status %ld",
__func__, status);
return -EPERM;
}
/* TDLS Off Channel, Enable tdls channel switch,
when their is only one tdls link and it supports */
numCurrTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (numCurrTdlsPeers == 1)
{
tSirMacAddr peerMac;
int channel;
mutex_lock(&pHddCtx->tdls_lock);
connPeer = wlan_hdd_tdls_get_connected_peer(pAdapter);
if (connPeer == NULL) {
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s connPeer is NULL", __func__);
return -EINVAL;
}
vos_mem_copy(peerMac, connPeer->peerMac, sizeof(tSirMacAddr));
channel = connPeer->peerParams.channel;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS channel switch "
"isOffChannelSupported %d "
"isOffChannelConfigured %d "
"isOffChannelEstablished %d",
__func__,
(connPeer ? connPeer->isOffChannelSupported : -1),
(connPeer ? connPeer->isOffChannelConfigured : -1),
(connPeer ? connPeer->isOffChannelEstablished : -1));
if ((connPeer) &&
(connPeer->isOffChannelSupported == TRUE) &&
(connPeer->isOffChannelConfigured == TRUE))
{
connPeer->isOffChannelEstablished = TRUE;
mutex_unlock(&pHddCtx->tdls_lock);
status = sme_SendTdlsChanSwitchReq(
WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
peerMac,
channel,
TDLS_OFF_CHANNEL_BW_OFFSET,
TDLS_CHANNEL_SWITCH_ENABLE);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to send TDLS switch channel req"));
}
}
else
mutex_unlock(&pHddCtx->tdls_lock);
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: TDLS channel switch request not sent "
"numCurrTdlsPeers %d ",
__func__, numCurrTdlsPeers);
}
}
else
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: TDLS Peer Station doesn't exist.", __func__);
}
if (numCurrTdlsPeers == 0) {
/* start TCP delack timer if TDLS is disable */
clear_bit(WLAN_TDLS_MODE, &pHddCtx->mode);
hdd_manage_delack_timer(pHddCtx);
}
}
break;
case NL80211_TDLS_TEARDOWN:
{
status = wlan_hdd_tdls_extctrl_deconfig_peer(pAdapter, peer);
if (0 != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Error in TDLS Teardown"));
return status;
}
break;
}
case NL80211_TDLS_SETUP:
{
status = wlan_hdd_tdls_extctrl_config_peer(pAdapter,
peer,
NULL,
NULL);
if (0 != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Error in TDLS Setup"));
return status;
}
break;
}
case NL80211_TDLS_DISCOVERY_REQ:
/* We don't support in-driver setup/teardown/discovery */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: Driver doesn't support in-driver setup/teardown/discovery "
,__func__);
return -ENOTSUPP;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: unsupported event",__func__);
return -ENOTSUPP;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
const u8 *peer,
#else
u8 *peer,
#endif
enum nl80211_tdls_operation oper)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_tdls_oper(wiphy, dev, peer, oper);
vos_ssr_unprotect(__func__);
return ret;
}
int wlan_hdd_cfg80211_send_tdls_discover_req(struct wiphy *wiphy,
struct net_device *dev, u8 *peer)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"tdls send discover req: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(peer));
#if TDLS_MGMT_VERSION2
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL, 0);
#else
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0, 0, NULL, 0);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL, 0);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0))
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL, 0);
#else
return wlan_hdd_cfg80211_tdls_mgmt(wiphy, dev, peer,
WLAN_TDLS_DISCOVERY_REQUEST, 1, 0, NULL, 0);
#endif
#endif /* KERNEL_VERSION */
}
#endif
#ifdef WLAN_FEATURE_GTK_OFFLOAD
/*
* FUNCTION: wlan_hdd_cfg80211_update_replayCounterCallback
* Callback rountine called upon receiving response for
* get offload info
*/
void wlan_hdd_cfg80211_update_replayCounterCallback(void *callbackContext,
tpSirGtkOffloadGetInfoRspParams pGtkOffloadGetInfoRsp)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *)callbackContext;
tANI_U8 tempReplayCounter[8];
hdd_station_ctx_t *pHddStaCtx;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return ;
}
if (NULL == pGtkOffloadGetInfoRsp)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pGtkOffloadGetInfoRsp is Null", __func__);
return ;
}
if (VOS_STATUS_SUCCESS != pGtkOffloadGetInfoRsp->ulStatus)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: wlan Failed to get replay counter value",
__func__);
return ;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* Update replay counter */
pHddStaCtx->gtkOffloadReqParams.ullKeyReplayCounter =
pGtkOffloadGetInfoRsp->ullKeyReplayCounter;
{
/* changing from little to big endian since supplicant
* works on big endian format
*/
int i;
tANI_U8 *p = (tANI_U8 *)&pGtkOffloadGetInfoRsp->ullKeyReplayCounter;
for (i = 0; i < 8; i++)
{
tempReplayCounter[7-i] = (tANI_U8)p[i];
}
}
/* Update replay counter to NL */
cfg80211_gtk_rekey_notify(pAdapter->dev, pGtkOffloadGetInfoRsp->bssId,
tempReplayCounter, GFP_KERNEL);
}
/*
* FUNCTION: __wlan_hdd_cfg80211_set_rekey_data
* This function is used to offload GTK rekeying job to the firmware.
*/
int __wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_gtk_rekey_data *data)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hdd_station_ctx_t *pHddStaCtx;
tHalHandle hHal;
int result;
tSirGtkOffloadParams hddGtkOffloadReqParams;
eHalStatus status = eHAL_STATUS_FAILURE;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_REKEY_DATA,
pAdapter->sessionId, pAdapter->device_mode));
result = wlan_hdd_validate_context(pHddCtx);
if (0 != result)
{
return result;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
if (NULL == hHal)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HAL context is Null!!!", __func__);
return -EAGAIN;
}
pHddStaCtx->gtkOffloadReqParams.ulFlags = GTK_OFFLOAD_ENABLE;
memcpy(pHddStaCtx->gtkOffloadReqParams.aKCK, data->kck, NL80211_KCK_LEN);
memcpy(pHddStaCtx->gtkOffloadReqParams.aKEK, data->kek, NL80211_KEK_LEN);
memcpy(pHddStaCtx->gtkOffloadReqParams.bssId, &pHddStaCtx->conn_info.bssId,
WNI_CFG_BSSID_LEN);
{
/* changing from big to little endian since driver
* works on little endian format
*/
tANI_U8 *p =
(tANI_U8 *)&pHddStaCtx->gtkOffloadReqParams.ullKeyReplayCounter;
int i;
for (i = 0; i < 8; i++)
{
p[7-i] = data->replay_ctr[i];
}
}
if (TRUE == pHddCtx->hdd_wlan_suspended)
{
/* if wlan is suspended, enable GTK offload directly from here */
memcpy(&hddGtkOffloadReqParams, &pHddStaCtx->gtkOffloadReqParams,
sizeof (tSirGtkOffloadParams));
status = sme_SetGTKOffload(hHal, &hddGtkOffloadReqParams,
pAdapter->sessionId);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_SetGTKOffload failed, returned %d",
__func__, status);
/* Need to clear any trace of key value in the memory.
* Thus zero out the memory even though it is local
* variable.
*/
vos_mem_zero(&hddGtkOffloadReqParams,
sizeof(hddGtkOffloadReqParams));
return status;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: sme_SetGTKOffload successfull", __func__);
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: wlan not suspended GTKOffload request is stored",
__func__);
}
/* Need to clear any trace of key value in the memory.
* Thus zero out the memory even though it is local
* variable.
*/
vos_mem_zero(&hddGtkOffloadReqParams,
sizeof(hddGtkOffloadReqParams));
EXIT();
return eHAL_STATUS_SUCCESS;
}
int wlan_hdd_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_gtk_rekey_data *data)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_rekey_data(wiphy, dev, data);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /*WLAN_FEATURE_GTK_OFFLOAD*/
/*
* FUNCTION: __wlan_hdd_cfg80211_set_mac_acl
* This function is used to set access control policy
*/
static int __wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy,
struct net_device *dev,
const struct cfg80211_acl_data *params)
{
int i;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_hostapd_state_t *pHostapdState;
tsap_Config_t *pConfig;
v_CONTEXT_t pVosContext = NULL;
hdd_context_t *pHddCtx;
int status;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
if (NULL == params)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: params is Null", __func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
pVosContext = pHddCtx->pvosContext;
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
if (NULL == pHostapdState)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: pHostapdState is Null", __func__);
return -EINVAL;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"acl policy: = %d"
"no acl entries = %d", params->acl_policy, params->n_acl_entries);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SET_MAC_ACL,
pAdapter->sessionId, pAdapter->device_mode));
if (WLAN_HDD_SOFTAP == pAdapter->device_mode)
{
pConfig = &pAdapter->sessionCtx.ap.sapConfig;
/* default value */
pConfig->num_accept_mac = 0;
pConfig->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)
{
pConfig->SapMacaddr_acl = eSAP_DENY_UNLESS_ACCEPTED;
}
else if (NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED == params->acl_policy)
{
pConfig->SapMacaddr_acl = eSAP_ACCEPT_UNLESS_DENIED;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:Acl Policy : %d is not supported",
__func__, params->acl_policy);
return -ENOTSUPP;
}
if (eSAP_DENY_UNLESS_ACCEPTED == pConfig->SapMacaddr_acl)
{
pConfig->num_accept_mac = params->n_acl_entries;
for (i = 0; i < params->n_acl_entries; i++)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"** Add ACL MAC entry %i in WhiletList :"
MAC_ADDRESS_STR, i,
MAC_ADDR_ARRAY(params->mac_addrs[i].addr));
vos_mem_copy(&pConfig->accept_mac[i], params->mac_addrs[i].addr,
sizeof(qcmacaddr));
}
}
else if (eSAP_ACCEPT_UNLESS_DENIED == pConfig->SapMacaddr_acl)
{
pConfig->num_deny_mac = params->n_acl_entries;
for (i = 0; i < params->n_acl_entries; i++)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"** Add ACL MAC entry %i in BlackList :"
MAC_ADDRESS_STR, i,
MAC_ADDR_ARRAY(params->mac_addrs[i].addr));
vos_mem_copy(&pConfig->deny_mac[i], params->mac_addrs[i].addr,
sizeof(qcmacaddr));
}
}
if (VOS_STATUS_SUCCESS != WLANSAP_SetMacACL(pVosContext, pConfig))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: SAP Set Mac Acl fail", __func__);
return -EINVAL;
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid device_mode = %s (%d)",
__func__, hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
return -EINVAL;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_set_mac_acl(struct wiphy *wiphy,
struct net_device *dev,
const struct cfg80211_acl_data *params)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_set_mac_acl(wiphy, dev, params);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_NL80211_TESTMODE
#ifdef FEATURE_WLAN_LPHB
void wlan_hdd_cfg80211_lphb_ind_handler
(
void *pAdapter,
void *indCont
)
{
tSirLPHBInd *lphbInd;
struct sk_buff *skb;
hdd_context_t *pHddCtxt;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"LPHB indication arrived");
if (pAdapter == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pAdapter is NULL\n",__func__);
return;
}
if (NULL == indCont)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"LPHB IND, invalid argument");
return;
}
pHddCtxt = (hdd_context_t *)pAdapter;
lphbInd = (tSirLPHBInd *)indCont;
skb = cfg80211_testmode_alloc_event_skb(
pHddCtxt->wiphy,
sizeof(tSirLPHBInd),
GFP_ATOMIC);
if (!skb)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"LPHB timeout, NL buffer alloc fail");
return;
}
if(nla_put_u32(skb, WLAN_HDD_TM_ATTR_CMD, WLAN_HDD_TM_CMD_WLAN_HB))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"WLAN_HDD_TM_ATTR_CMD put fail");
goto nla_put_failure;
}
if(nla_put_u32(skb, WLAN_HDD_TM_ATTR_TYPE, lphbInd->protocolType))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"WLAN_HDD_TM_ATTR_TYPE put fail");
goto nla_put_failure;
}
if(nla_put(skb, WLAN_HDD_TM_ATTR_DATA,
sizeof(tSirLPHBInd), lphbInd))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"WLAN_HDD_TM_ATTR_DATA put fail");
goto nla_put_failure;
}
cfg80211_testmode_event(skb, GFP_ATOMIC);
return;
nla_put_failure:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"NLA Put fail");
kfree_skb(skb);
return;
}
#endif /* FEATURE_WLAN_LPHB */
static int __wlan_hdd_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
{
struct nlattr *tb[WLAN_HDD_TM_ATTR_MAX + 1];
int err = 0;
#ifdef FEATURE_WLAN_LPHB
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
eHalStatus smeStatus;
ENTER();
err = wlan_hdd_validate_context(pHddCtx);
if (0 != err)
{
return err;
}
#endif /* FEATURE_WLAN_LPHB */
err = nla_parse(tb, WLAN_HDD_TM_ATTR_MAX, data, len, wlan_hdd_tm_policy);
if (err)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s Testmode INV ATTR", __func__);
return err;
}
if (!tb[WLAN_HDD_TM_ATTR_CMD])
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s Testmode INV CMD", __func__);
return -EINVAL;
}
MTRACE(vos_trace(VOS_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;
tSirLPHBReq *hb_params = NULL;
tSirLPHBReq *hb_params_temp = NULL;
if (!tb[WLAN_HDD_TM_ATTR_DATA])
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s Testmode INV DATA", __func__);
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)) {
hddLog(LOGE, FL("buf_len=%d exceeded hb_params size limit"),
buf_len);
return -ERANGE;
}
hb_params_temp =(tSirLPHBReq *)buf;
if ((hb_params_temp->cmd == LPHB_SET_TCP_PARAMS_INDID) &&
(hb_params_temp->params.lphbTcpParamReq.timePeriodSec == 0))
return -EINVAL;
hb_params = (tSirLPHBReq *)vos_mem_malloc(sizeof(tSirLPHBReq));
if (NULL == hb_params)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s Request Buffer Alloc Fail", __func__);
return -EINVAL;
}
vos_mem_zero(hb_params, sizeof(tSirLPHBReq));
vos_mem_copy(hb_params, buf, buf_len);
smeStatus = sme_LPHBConfigReq((tHalHandle)(pHddCtx->hHal),
hb_params,
wlan_hdd_cfg80211_lphb_ind_handler);
if (eHAL_STATUS_SUCCESS != smeStatus)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"LPHB Config Fail, disable");
vos_mem_free(hb_params);
}
return 0;
}
#endif /* FEATURE_WLAN_LPHB */
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: unsupported event",__func__);
return -EOPNOTSUPP;
}
EXIT();
return err;
}
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)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_testmode(wiphy, data, len);
vos_ssr_unprotect(__func__);
return ret;
}
#endif /* CONFIG_NL80211_TESTMODE */
extern void hdd_set_wlan_suspend_mode(bool suspend);
static int __wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev,
int idx, struct survey_info *survey)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
hdd_station_ctx_t *pHddStaCtx;
tHalHandle halHandle;
v_U32_t channel = 0, freq = 0; /* Initialization Required */
v_S7_t snr,rssi;
int status, i, j, filled = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
if (NULL == wiphy)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: wiphy is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (0 == pHddCtx->cfg_ini->fEnableSNRMonitoring ||
0 != pAdapter->survey_idx ||
eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
/* The survey dump ops when implemented completely is expected to
* return a survey of all channels and the ops is called by the
* kernel with incremental values of the argument 'idx' till it
* returns -ENONET. But we can only support the survey for the
* operating channel for now. survey_idx is used to track
* that the ops is called only once and then return -ENONET for
* the next iteration
*/
pAdapter->survey_idx = 0;
return -ENONET;
}
if (VOS_TRUE == pHddStaCtx->hdd_ReassocScenario)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Roaming in progress, hence return ", __func__);
return -ENONET;
}
halHandle = WLAN_HDD_GET_HAL_CTX(pAdapter);
wlan_hdd_get_snr(pAdapter, &snr);
wlan_hdd_get_rssi(pAdapter, &rssi);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_DUMP_SURVEY,
pAdapter->sessionId, pAdapter->device_mode));
sme_GetOperationChannel(halHandle, &channel, pAdapter->sessionId);
hdd_wlan_get_freq(channel, &freq);
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++)
{
if (NULL == wiphy->bands[i])
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: wiphy->bands[i] is NULL, i = %d", __func__, i);
continue;
}
for (j = 0; j < wiphy->bands[i]->n_channels; j++)
{
struct ieee80211_supported_band *band = wiphy->bands[i];
if (band->channels[j].center_freq == (v_U16_t)freq)
{
survey->channel = &band->channels[j];
/* The Rx BDs contain SNR values in dB for the received frames
* while the supplicant expects noise. So we calculate and
* return the value of noise (dBm)
* SNR (dB) = RSSI (dBm) - NOISE (dBm)
*/
survey->noise = rssi - snr;
survey->filled = SURVEY_INFO_NOISE_DBM;
filled = 1;
}
}
}
if (filled)
pAdapter->survey_idx = 1;
else
{
pAdapter->survey_idx = 0;
return -ENONET;
}
EXIT();
return 0;
}
static int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev,
int idx, struct survey_info *survey)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_dump_survey(wiphy, dev, idx, survey);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_resume_wlan
* this is called when cfg80211 driver resume
* driver updates latest sched_scan scan result(if any) to cfg80211 database
*/
int __wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
hdd_adapter_t *pAdapter;
hdd_adapter_list_node_t *pAdapterNode, *pNext;
VOS_STATUS status = VOS_STATUS_SUCCESS;
ENTER();
if (0 != wlan_hdd_validate_context(pHddCtx))
{
return 0;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_CFG80211_RESUME_WLAN,
NO_SESSION, pHddCtx->isWiphySuspended));
if (pHddCtx->is_ap_mode_wow_supported)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Resume SoftAP", __func__);
hdd_set_wlan_suspend_mode(false);
}
spin_lock(&pHddCtx->schedScan_lock);
pHddCtx->isWiphySuspended = FALSE;
if (TRUE != pHddCtx->isSchedScanUpdatePending)
{
spin_unlock(&pHddCtx->schedScan_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Return resume is not due to PNO indication", __func__);
return 0;
}
// Reset flag to avoid updatating cfg80211 data old results again
pHddCtx->isSchedScanUpdatePending = FALSE;
spin_unlock(&pHddCtx->schedScan_lock);
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if ( (NULL != pAdapter) &&
(WLAN_HDD_INFRA_STATION == pAdapter->device_mode) )
{
if (0 != wlan_hdd_cfg80211_update_bss(pHddCtx->wiphy, pAdapter))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: NO SCAN result", __func__);
}
else
{
/* Acquire wakelock to handle the case where APP's tries to
* suspend immediately after updating the scan results. Whis
* results in app's is in suspended state and not able to
* process the connect request to AP
*/
hdd_prevent_suspend_timeout(2000,
WIFI_POWER_EVENT_WAKELOCK_RESUME_WLAN);
cfg80211_sched_scan_results(pHddCtx->wiphy);
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : cfg80211 scan result database updated", __func__);
EXIT();
return 0;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Failed to find Adapter", __func__);
EXIT();
return 0;
}
int wlan_hdd_cfg80211_resume_wlan(struct wiphy *wiphy)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_resume_wlan(wiphy);
vos_ssr_unprotect(__func__);
return ret;
}
/*
* FUNCTION: __wlan_hdd_cfg80211_suspend_wlan
* this is called when cfg80211 driver suspends
*/
int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
hdd_context_t *pHddCtx = wiphy_priv(wiphy);
int ret = 0;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
if (pHddCtx->is_ap_mode_wow_supported) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Suspend SoftAP", __func__);
hdd_set_wlan_suspend_mode(true);
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_SUSPEND_WLAN,
NO_SESSION, pHddCtx->isWiphySuspended));
pHddCtx->isWiphySuspended = TRUE;
EXIT();
return 0;
}
int wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
struct cfg80211_wowlan *wow)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, wow);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef FEATURE_OEM_DATA_SUPPORT
static void wlan_hdd_cfg80211_oem_data_rsp_ind_new(void *ctx,
void *pMsg, tANI_U32 evLen)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
return;
}
if (!pMsg)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("pMsg is null"));
return;
}
send_oem_data_rsp_msg(evLen, pMsg);
EXIT();
return;
}
void wlan_hdd_cfg80211_oemdata_callback(void *ctx, const tANI_U16 evType,
void *pMsg, tANI_U32 evLen)
{
hdd_context_t *pHddCtx = (hdd_context_t *)ctx;
ENTER();
if (wlan_hdd_validate_context(pHddCtx)) {
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Rcvd Event (%d) evLen %d"), evType, evLen);
switch(evType) {
case SIR_HAL_START_OEM_DATA_RSP_IND_NEW:
wlan_hdd_cfg80211_oem_data_rsp_ind_new(ctx, pMsg, evLen);
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, FL("invalid event type %d "), evType);
break;
}
EXIT();
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)) || \
defined(CFG80211_ABORT_SCAN)
/**
* __wlan_hdd_cfg80211_abort_scan() - cfg80211 abort scan api
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wireless device structure
*
* This function is used to abort an ongoing scan
*
* Return: None
*/
static void __wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
int ret;
ENTER();
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_FATAL, FL("HDD adapter is NULL"));
return;
}
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return;
wlan_hdd_scan_abort(adapter);
return;
}
/**
* wlan_hdd_cfg80211_abort_scan - cfg80211 abort scan api
* @wiphy: Pointer to wiphy
* @wdev: Pointer to wireless device structure
*
* Return: None
*/
void wlan_hdd_cfg80211_abort_scan(struct wiphy *wiphy,
struct wireless_dev *wdev)
{
vos_ssr_protect(__func__);
__wlan_hdd_cfg80211_abort_scan(wiphy, wdev);
vos_ssr_unprotect(__func__);
return;
}
#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)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx;
uint8_t channel;
int ret;
ptSapContext sap_ctx;
v_CONTEXT_t vos_ctx;
hddLog(LOGE, FL("Set Freq %d"), csa_params->chandef.chan->center_freq);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return ret;
vos_ctx = (WLAN_HDD_GET_CTX(adapter))->pvosContext;
if (!vos_ctx) {
hddLog(LOGE, FL("Vos ctx is null"));
return -EINVAL;
}
if (WLAN_HDD_SOFTAP != adapter->device_mode)
return -ENOTSUPP;
sap_ctx = VOS_GET_SAP_CB(vos_ctx);
if (!sap_ctx) {
hddLog(LOGE, FL("sap_ctx is NULL"));
return -EINVAL;
}
ret = wlansap_chk_n_set_chan_change_in_progress(sap_ctx);
if (ret)
return ret;
INIT_COMPLETION(sap_ctx->ecsa_info.chan_switch_comp);
channel = vos_freq_to_chan(csa_params->chandef.chan->center_freq);
ret = wlansap_set_channel_change(vos_ctx, channel, false);
if (ret) {
wlansap_reset_chan_change_in_progress(sap_ctx);
complete(&sap_ctx->ecsa_info.chan_switch_comp);
}
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 ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_channel_switch(wiphy, dev, csa_params);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
/* cfg80211_ops */
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,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
.add_beacon = wlan_hdd_cfg80211_add_beacon,
.del_beacon = wlan_hdd_cfg80211_del_beacon,
.set_beacon = wlan_hdd_cfg80211_set_beacon,
#else
.start_ap = wlan_hdd_cfg80211_start_ap,
.change_beacon = wlan_hdd_cfg80211_change_beacon,
.stop_ap = wlan_hdd_cfg80211_stop_ap,
#endif
.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,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
.set_channel = wlan_hdd_cfg80211_set_channel,
#endif
.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,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
.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,
#endif
.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,
#ifdef FEATURE_WLAN_LFR
.set_pmksa = wlan_hdd_cfg80211_set_pmksa,
.del_pmksa = wlan_hdd_cfg80211_del_pmksa,
.flush_pmksa = wlan_hdd_cfg80211_flush_pmksa,
#endif
#if defined(WLAN_FEATURE_VOWIFI_11R) && defined(KERNEL_SUPPORT_11R_CFG80211)
.update_ft_ies = wlan_hdd_cfg80211_update_ft_ies,
#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
.dump_survey = wlan_hdd_cfg80211_dump_survey,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0)) || \
defined(CFG80211_ABORT_SCAN)
.abort_scan = wlan_hdd_cfg80211_abort_scan,
#endif
#ifdef CHANNEL_SWITCH_SUPPORTED
.channel_switch = wlan_hdd_cfg80211_channel_switch,
#endif
};