blob: d29c92d8335edfb0887a37e403f1f151240a9fba [file] [log] [blame] [edit]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**========================================================================
\file wlan_hdd_hostapd.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
-------- --- --------------------------------------------------------
04/5/09 Shailender Created module.
06/03/10 js - Added support to hostapd driven deauth/disassoc/mic failure
==========================================================================*/
/*--------------------------------------------------------------------------
Include Files
------------------------------------------------------------------------*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/wireless.h>
#include <linux/semaphore.h>
#include <linux/compat.h>
#include <vos_api.h>
#include <vos_sched.h>
#include <linux/etherdevice.h>
#include <wlan_hdd_includes.h>
#include <qc_sap_ioctl.h>
#include <wlan_hdd_hostapd.h>
#include <sapApi.h>
#include <sapInternal.h>
#include <wlan_qct_tl.h>
#include <wlan_hdd_softap_tx_rx.h>
#include <wlan_hdd_main.h>
#include <linux/netdevice.h>
#include <linux/mmc/sdio_func.h>
#include "wlan_nlink_common.h"
#include "wlan_hdd_p2p.h"
#ifdef IPA_OFFLOAD
#include <wlan_hdd_ipa.h>
#endif
#include "cfgApi.h"
#include "wni_cfg.h"
#include "wlan_hdd_misc.h"
#include <vos_utils.h>
#include "vos_cnss.h"
#include "tl_shim.h"
#include "wma.h"
#ifdef WLAN_DEBUG
#include "wma_api.h"
#endif
extern int process_wma_set_command(int sessid, int paramid,
int sval, int vpdev);
#include "wlan_hdd_trace.h"
#include "vos_types.h"
#include "vos_trace.h"
#include "adf_trace.h"
#include "wlan_hdd_cfg.h"
#include <wlan_hdd_wowl.h>
#include "wlan_hdd_tsf.h"
#include "wlan_hdd_oemdata.h"
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
#include <vos_utils.h>
#endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
#define IS_UP(_dev) \
(((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
#define IS_UP_AUTO(_ic) \
(IS_UP((_ic)->ic_dev) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
#define WE_WLAN_VERSION 1
#define WE_GET_STA_INFO_SIZE 30
/* WEXT limitation: MAX allowed buf len for any *
* IW_PRIV_TYPE_CHAR is 2Kbytes *
*/
#define WE_SAP_MAX_STA_INFO 0x7FF
#define RC_2_RATE_IDX(_rc) ((_rc) & 0x7)
#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
#define RC_2_RATE_IDX_11AC(_rc) ((_rc) & 0xf)
#define HT_RC_2_STREAMS_11AC(_rc) ((((_rc) & 0x30) >> 4) + 1)
#define SAP_24GHZ_CH_COUNT (14)
#define ACS_SCAN_EXPIRY_TIMEOUT_S 4
/* EID byte + length byte + four byte WiFi OUI */
#define DOT11F_EID_HEADER_LEN (6)
/* Data rate 100KBPS based on IE Index */
struct index_data_rate_type {
uint8_t beacon_rate_index;
uint16_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 index_data_rate_type supported_data_rate[] = {
/* IDX HI HM LM LO (RSSI-based index */
{2, { 10, 10, 10, 0} },
{4, { 20, 20, 10, 0} },
{11, { 55, 20, 10, 0} },
{12, { 60, 55, 20, 0} },
{18, { 90, 55, 20, 0} },
{22, {110, 55, 20, 0} },
{24, {120, 90, 60, 0} },
{36, {180, 120, 60, 0} },
{44, {220, 180, 60, 0} },
{48, {240, 180, 90, 0} },
{66, {330, 180, 90, 0} },
{72, {360, 240, 90, 0} },
{96, {480, 240, 120, 0} },
{108, {540, 240, 120, 0} }
};
/* MCS Based rate table */
/* HT MCS parameters with Nss = 1 */
static struct index_data_rate_type supported_mcs_rate_nss1[] = {
/* MCS L20 L40 S20 S40 */
{0, { 65, 135, 72, 150} },
{1, { 130, 270, 144, 300} },
{2, { 195, 405, 217, 450} },
{3, { 260, 540, 289, 600} },
{4, { 390, 810, 433, 900} },
{5, { 520, 1080, 578, 1200} },
{6, { 585, 1215, 650, 1350} },
{7, { 650, 1350, 722, 1500} }
};
/* HT MCS parameters with Nss = 2 */
static struct index_data_rate_type supported_mcs_rate_nss2[] = {
/* MCS L20 L40 S20 S40 */
{0, {130, 270, 144, 300} },
{1, {260, 540, 289, 600} },
{2, {390, 810, 433, 900} },
{3, {520, 1080, 578, 1200} },
{4, {780, 1620, 867, 1800} },
{5, {1040, 2160, 1156, 2400} },
{6, {1170, 2430, 1300, 2700} },
{7, {1300, 2700, 1444, 3000} }
};
#ifdef WLAN_FEATURE_11AC
#define DATA_RATE_11AC_MCS_MASK 0x03
enum data_rate_11ac_max_mcs {
DATA_RATE_11AC_MAX_MCS_7,
DATA_RATE_11AC_MAX_MCS_8,
DATA_RATE_11AC_MAX_MCS_9,
DATA_RATE_11AC_MAX_MCS_NA
};
struct index_vht_data_rate_type {
uint8_t beacon_rate_index;
uint16_t supported_VHT80_rate[2];
uint16_t supported_VHT40_rate[2];
uint16_t supported_VHT20_rate[2];
};
/* MCS Based VHT rate table */
/* MCS parameters with Nss = 1*/
static struct index_vht_data_rate_type supported_vht_mcs_rate_nss1[] = {
/* MCS L80 S80 L40 S40 L20 S40*/
{0, {293, 325}, {135, 150}, {65, 72} },
{1, {585, 650}, {270, 300}, {130, 144} },
{2, {878, 975}, {405, 450}, {195, 217} },
{3, {1170, 1300}, {540, 600}, {260, 289} },
{4, {1755, 1950}, {810, 900}, {390, 433} },
{5, {2340, 2600}, {1080, 1200}, {520, 578} },
{6, {2633, 2925}, {1215, 1350}, {585, 650} },
{7, {2925, 3250}, {1350, 1500}, {650, 722} },
{8, {3510, 3900}, {1620, 1800}, {780, 867} },
{9, {3900, 4333}, {1800, 2000}, {780, 867} }
};
/*MCS parameters with Nss = 2*/
static struct index_vht_data_rate_type supported_vht_mcs_rate_nss2[] = {
/* MCS L80 S80 L40 S40 L20 S40*/
{0, {585, 650}, {270, 300}, {130, 144} },
{1, {1170, 1300}, {540, 600}, {260, 289} },
{2, {1755, 1950}, {810, 900}, {390, 433} },
{3, {2340, 2600}, {1080, 1200}, {520, 578} },
{4, {3510, 3900}, {1620, 1800}, {780, 867} },
{5, {4680, 5200}, {2160, 2400}, {1040, 1156} },
{6, {5265, 5850}, {2430, 2700}, {1170, 1300} },
{7, {5850, 6500}, {2700, 3000}, {1300, 1444} },
{8, {7020, 7800}, {3240, 3600}, {1560, 1733} },
{9, {7800, 8667}, {3600, 4000}, {1560, 1733} }
};
#endif /* WLAN_FEATURE_11AC */
/*---------------------------------------------------------------------------
* Function definitions
*-------------------------------------------------------------------------*/
/**---------------------------------------------------------------------------
\brief hdd_hostapd_channel_wakelock_init
\param - Pointer to HDD context
\return - None
--------------------------------------------------------------------------*/
void hdd_hostapd_channel_wakelock_init(hdd_context_t *pHddCtx)
{
/* Initialize the wakelock */
vos_wake_lock_init(&pHddCtx->sap_dfs_wakelock, "sap_dfs_wakelock");
atomic_set(&pHddCtx->sap_dfs_ref_cnt, 0);
}
/**---------------------------------------------------------------------------
\brief hdd_hostapd_channel_allow_suspend - Allow suspend in a channel.
Called when,
1. BSS stopped
2. Channel switch
\param - pAdapter, channel
\return - None
--------------------------------------------------------------------------*/
void hdd_hostapd_channel_allow_suspend(hdd_adapter_t *pAdapter,
u_int8_t channel)
{
hdd_context_t *pHddCtx = (hdd_context_t*)(pAdapter->pHddCtx);
hdd_hostapd_state_t *pHostapdState =
WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
hddLog(LOG1, FL("bssState: %d, channel: %d, dfs_ref_cnt: %d"),
pHostapdState->bssState, channel,
atomic_read(&pHddCtx->sap_dfs_ref_cnt));
/* Return if BSS is already stopped */
if (pHostapdState->bssState == BSS_STOP)
return;
/* Release wakelock when no more DFS channels are used */
if (NV_CHANNEL_DFS == vos_nv_getChannelEnabledState(channel)) {
if (atomic_dec_and_test(&pHddCtx->sap_dfs_ref_cnt)) {
hddLog(LOGE, FL("DFS: allowing suspend (chan %d)"), channel);
vos_wake_lock_release(&pHddCtx->sap_dfs_wakelock,
WIFI_POWER_EVENT_WAKELOCK_DFS);
vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.dfs);
}
}
}
/**---------------------------------------------------------------------------
\brief hdd_hostapd_channel_prevent_suspend - Prevent suspend in a channel.
Called when,
1. BSS started
2. Channel switch
\param - pAdapter, channel
\return - None
--------------------------------------------------------------------------*/
void hdd_hostapd_channel_prevent_suspend(hdd_adapter_t *pAdapter,
u_int8_t channel)
{
hdd_context_t *pHddCtx = (hdd_context_t*)(pAdapter->pHddCtx);
hdd_hostapd_state_t *pHostapdState =
WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
hddLog(LOG1, FL("bssState: %d, channel: %d, dfs_ref_cnt: %d"),
pHostapdState->bssState, channel,
atomic_read(&pHddCtx->sap_dfs_ref_cnt));
/* Return if BSS is already started && wakelock is acquired */
if ((pHostapdState->bssState == BSS_START) &&
(atomic_read(&pHddCtx->sap_dfs_ref_cnt) >= 1))
return;
/* Acquire wakelock if we have at least one DFS channel in use */
if (NV_CHANNEL_DFS == vos_nv_getChannelEnabledState(channel)) {
if (atomic_inc_return(&pHddCtx->sap_dfs_ref_cnt) == 1) {
hddLog(LOGE, FL("DFS: preventing suspend (chan %d)"), channel);
vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.dfs);
vos_wake_lock_acquire(&pHddCtx->sap_dfs_wakelock,
WIFI_POWER_EVENT_WAKELOCK_DFS);
}
}
}
/**---------------------------------------------------------------------------
\brief hdd_hostapd_channel_wakelock_deinit
\param - Pointer to HDD context
\return - None
--------------------------------------------------------------------------*/
void hdd_hostapd_channel_wakelock_deinit(hdd_context_t *pHddCtx)
{
if (atomic_read(&pHddCtx->sap_dfs_ref_cnt)) {
/* Release wakelock */
vos_wake_lock_release(&pHddCtx->sap_dfs_wakelock,
WIFI_POWER_EVENT_WAKELOCK_DRIVER_EXIT);
/* Reset the reference count */
atomic_set(&pHddCtx->sap_dfs_ref_cnt, 0);
hddLog(LOGE, FL("DFS: allowing suspend"));
}
/* Destroy lock */
vos_wake_lock_destroy(&pHddCtx->sap_dfs_wakelock);
}
#ifdef FEATURE_WLAN_SUB_20_MHZ
/**
* hdd_hostapd_sub20_channelwidth_can_switch() - check
* channel width switch to 5/10M condition
* @adapter: pointer to HDD context
* @sub20_channel_width: 5MHz/10MHz channel width
*
* Return: true or false
*/
bool hdd_hostapd_sub20_channelwidth_can_switch(
hdd_adapter_t *adapter, uint32_t *sub20_channel_width)
{
int i;
int sta_count = 0;
uint8_t sap_s20_caps;
uint8_t sap_s20_config;
uint8_t sta_s20_caps = SUB20_MODE_NONE;
tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter);
tSmeConfigParams *sme_config;
hdd_station_info_t *sta;
hdd_ap_ctx_t *ap = WLAN_HDD_GET_AP_CTX_PTR(adapter);
sme_config = vos_mem_malloc(sizeof(*sme_config));
if (!sme_config) {
hddLog(LOGE, FL("mem alloc failed for sme_config"));
return false;
}
vos_mem_zero(sme_config, sizeof(*sme_config));
sme_GetConfigParam(hal_ptr, sme_config);
sap_s20_caps = sme_config->sub20_dynamic_channelwidth;
sap_s20_config = sme_config->sub20_config_info;
vos_mem_free(sme_config);
if (sap_s20_caps == SUB20_MODE_NONE ||
sap_s20_config == CFG_SUB_20_CHANNEL_WIDTH_MANUAL) {
hddLog(LOGE, FL("sub20 not switch"));
return false;
}
spin_lock_bh(&adapter->staInfo_lock);
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
sta = &adapter->aStaInfo[i];
if (sta->isUsed && (ap->uBCStaId != i)) {
sta_count++;
sta_s20_caps |=
sta->sub20_dynamic_channelwidth;
}
}
spin_unlock_bh(&adapter->staInfo_lock);
if (sta_count != 1) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%d STAs connected with sub20 Channelwidth %d",
sta_count, sta_s20_caps);
return false;
}
*sub20_channel_width = sta_s20_caps & sap_s20_caps;
if (*sub20_channel_width == (SUB20_MODE_5MHZ | SUB20_MODE_10MHZ))
*sub20_channel_width = SUB20_MODE_10MHZ;
if (*sub20_channel_width != 0)
return true;
else
return false;
}
/**
* hdd_hostapd_sub20_channelwidth_can_restore() - check
* channel width switch to normal condition
* @adapter: pointer to HDD context
*
* Return: true or false
*/
bool hdd_hostapd_sub20_channelwidth_can_restore(
hdd_adapter_t *adapter)
{
int i;
int sta_count = 0;
uint8_t sap_s20_caps;
uint8_t sta_s20_caps = SUB20_MODE_NONE;
tHalHandle hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter);
tSmeConfigParams *sme_config;
hdd_station_info_t *sta;
hdd_ap_ctx_t *ap = WLAN_HDD_GET_AP_CTX_PTR(adapter);
sme_config = vos_mem_malloc(sizeof(*sme_config));
if (!sme_config) {
hddLog(LOGE, FL("mem alloc failed for sme_config"));
return false;
}
vos_mem_zero(sme_config, sizeof(*sme_config));
sme_GetConfigParam(hal_ptr, sme_config);
sap_s20_caps = sme_config->sub20_dynamic_channelwidth;
vos_mem_free(sme_config);
if (sap_s20_caps == SUB20_MODE_NONE) {
hddLog(LOGE, FL("sub20 none"));
return false;
}
spin_lock_bh(&adapter->staInfo_lock);
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
sta = &adapter->aStaInfo[i];
if (sta->isUsed && (ap->uBCStaId != i)) {
sta_count++;
sta_s20_caps |=
sta->sub20_dynamic_channelwidth;
}
}
spin_unlock_bh(&adapter->staInfo_lock);
if (sta_count != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%d STAs connected with sub20 Channelwidth %d",
sta_count, sta_s20_caps);
return false;
} else {
return true;
}
}
/**
* hdd_sub20_channelwidth_can_set() - check
* channel width manual switch to 5/10M condition
* @adapter: pointer to HDD context
* @sub20_channel_width: new channel width
*
* Return: true or false
*/
bool hdd_sub20_channelwidth_can_set(
hdd_adapter_t *adapter, uint32_t sub20_channel_width)
{
int i;
uint32_t sta_count = 0;
uint8_t sap_s20_config;
uint8_t sta_s20_caps = SUB20_MODE_10MHZ|SUB20_MODE_5MHZ;
tHalHandle hal_ptr;
tSmeConfigParams *sme_config;
hdd_station_info_t *sta;
hdd_ap_ctx_t *ap;
bool channel_support_sub20 = true;
enum phy_ch_width phy_sub20_channel_width = CH_WIDTH_INVALID;
hdd_station_ctx_t *hddstactx;
if (adapter == NULL) {
hddLog(LOGE, FL("adapter NULL"));
return false;
}
hal_ptr = WLAN_HDD_GET_HAL_CTX(adapter);
ap = WLAN_HDD_GET_AP_CTX_PTR(adapter);
sme_config = vos_mem_malloc(sizeof(*sme_config));
if (!sme_config) {
hddLog(LOGE, FL("mem alloc failed for sme_config"));
return false;
}
vos_mem_zero(sme_config, sizeof(*sme_config));
sme_GetConfigParam(hal_ptr, sme_config);
sap_s20_config = sme_config->sub20_config_info;
vos_mem_free(sme_config);
sme_config = NULL;
if (sap_s20_config != CFG_SUB_20_CHANNEL_WIDTH_MANUAL) {
hddLog(LOGE, FL("ini unsupport manual set sub20"));
return false;
}
switch (sub20_channel_width) {
case SUB20_MODE_5MHZ:
phy_sub20_channel_width = CH_WIDTH_5MHZ;
break;
case SUB20_MODE_10MHZ:
phy_sub20_channel_width = CH_WIDTH_10MHZ;
break;
case SUB20_MODE_NONE:
if (WLAN_HDD_SOFTAP == adapter->device_mode)
return true;
break;
default:
return false;
}
if (WLAN_HDD_INFRA_STATION == adapter->device_mode) {
hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
if (hddstactx == NULL) {
hddLog(LOGE, FL("hdd hddstactx is null"));
return false;
}
if (hdd_connIsConnected(hddstactx)) {
hddLog(LOGE, FL("sta in Connected state!"));
return false;
}
hddLog(LOGE, FL("sta can set sub20"));
return true;
}
channel_support_sub20 =
vos_is_channel_support_sub20(ap->operatingChannel,
phy_sub20_channel_width,
0);
if (!channel_support_sub20) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("ch%d width%d unsupport by reg domain"),
ap->operatingChannel, phy_sub20_channel_width);
return false;
}
spin_lock_bh(&adapter->staInfo_lock);
for (i = 0; i < WLAN_MAX_STA_COUNT; i++) {
sta = &adapter->aStaInfo[i];
if (sta->isUsed && (ap->uBCStaId != i)) {
sta_count++;
sta_s20_caps &=
sta->sub20_dynamic_channelwidth;
}
}
spin_unlock_bh(&adapter->staInfo_lock);
if (sta_count >= 1 && !(sta_s20_caps & sub20_channel_width)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%d STAs connected with sub20 Channelwidth %d",
sta_count, sta_s20_caps);
return false;
}
return true;
}
#endif
/**
* __hdd_hostapd_open() - HDD Open function for hostapd interface
* @dev: pointer to net device
*
* This is called in response to ifconfig up
*
* Return: 0 on success, error number otherwise
*/
static int __hdd_hostapd_open(struct net_device *dev)
{
hdd_adapter_t *pAdapter = netdev_priv(dev);
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_HOSTAPD_OPEN_REQUEST, NO_SESSION, 0));
if (WLAN_HDD_GET_CTX(pAdapter)->isLoadInProgress ||
WLAN_HDD_GET_CTX(pAdapter)->isUnloadInProgress)
{
hddLog(LOGE, FL("Driver load/unload in progress, ignore adapter open"));
goto done;
}
//Enable all Tx queues
hddLog(LOG1, FL("Enabling queues"));
wlan_hdd_netif_queue_control(pAdapter,
WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
WLAN_CONTROL_PATH);
done:
EXIT();
return 0;
}
/**
* hdd_hostapd_open() - SSR wrapper for __hdd_hostapd_open
* @dev: pointer to net device
*
* Return: 0 on success, error number otherwise
*/
static int hdd_hostapd_open(struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hostapd_open(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __hdd_hostapd_stop() - HDD stop function for hostapd interface
* @dev: pointer to net_device
*
* This is called in response to ifconfig down
*
* Return: 0 on success, error number otherwise
*/
static int __hdd_hostapd_stop(struct net_device *dev)
{
ENTER();
if (NULL != dev) {
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
//Stop all tx queues
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(adapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
}
EXIT();
return 0;
}
/**
* hdd_hostapd_stop() - SSR wrapper for__hdd_hostapd_stop
* @dev: pointer to net_device
*
* This is called in response to ifconfig down
*
* Return: 0 on success, error number otherwise
*/
int hdd_hostapd_stop(struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hostapd_stop(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __hdd_hostapd_uninit() - HDD uninit function
* @dev: pointer to net_device
*
* This is called during the netdev unregister to uninitialize all data
* associated with the device
*
* Return: 0 on success, error number otherwise
*/
static void __hdd_hostapd_uninit(struct net_device *dev)
{
hdd_adapter_t *adapter = netdev_priv(dev);
hdd_context_t *hdd_ctx;
ENTER();
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hddLog(LOGE, FL("Invalid magic"));
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (NULL == hdd_ctx) {
hddLog(LOGE, FL("NULL hdd_ctx"));
return;
}
hdd_deinit_adapter(hdd_ctx, adapter, true);
/* after uninit our adapter structure will no longer be valid */
adapter->dev = NULL;
adapter->magic = 0;
EXIT();
}
/**
* hdd_hostapd_uninit() - SSR wrapper for __hdd_hostapd_uninit
* @dev: pointer to net_device
*
* Return: 0 on success, error number otherwise
*/
static void hdd_hostapd_uninit(struct net_device *dev)
{
vos_ssr_protect(__func__);
__hdd_hostapd_uninit(dev);
vos_ssr_unprotect(__func__);
}
/**
* __hdd_hostapd_change_mtu() - change mtu
* @dev: pointer to net_device
* @new_mtu: new mtu
*
* Return: 0 on success, error number otherwise
*/
static int __hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu)
{
return 0;
}
/**
* hdd_hostapd_change_mtu() - SSR wrapper for __hdd_hostapd_change_mtu
* @dev: pointer to net_device
* @new_mtu: new mtu
*
* Return: 0 on success, error number otherwise
*/
static int hdd_hostapd_change_mtu(struct net_device *dev, int new_mtu)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hostapd_change_mtu(dev, new_mtu);
vos_ssr_unprotect(__func__);
return ret;
}
static int hdd_hostapd_driver_command(hdd_adapter_t *pAdapter,
hdd_priv_data_t *priv_data)
{
tANI_U8 *command = NULL;
int ret = 0;
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
/*
* Note that valid pointers are provided by caller
*/
ENTER();
if (priv_data->total_len <= 0 ||
priv_data->total_len > HOSTAPD_IOCTL_COMMAND_STRLEN_MAX)
{
/* below we allocate one more byte for command buffer.
* To avoid addition overflow total_len should be
* smaller than INT_MAX. */
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: integer out of range len %d",
__func__, priv_data->total_len);
ret = -EFAULT;
goto exit;
}
/* Allocate +1 for '\0' */
command = vos_mem_malloc((priv_data->total_len + 1));
if (!command)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to allocate memory", __func__);
ret = -ENOMEM;
goto exit;
}
if (copy_from_user(command, priv_data->buf, priv_data->total_len))
{
ret = -EFAULT;
goto exit;
}
/* Make sure the command is NUL-terminated */
command[priv_data->total_len] = '\0';
hddLog(VOS_TRACE_LEVEL_INFO,
"***HOSTAPD*** : Received %s cmd from Wi-Fi GUI***", command);
if (strncmp(command, "P2P_SET_NOA", 11) == 0)
{
hdd_setP2pNoa(pAdapter->dev, command);
}
else if (strncmp(command, "P2P_SET_PS", 10) == 0)
{
hdd_setP2pOpps(pAdapter->dev, command);
}
else if (strncmp(command, "MIRACAST", 8) == 0)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Received MIRACAST command", __func__);
ret = hdd_drv_cmd_validate(command, 8);
if (ret)
goto exit;
ret = hdd_set_miracast_mode(pAdapter, command);
}
exit:
if (command)
{
vos_mem_free(command);
}
EXIT();
return ret;
}
#ifdef CONFIG_COMPAT
static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter,
struct ifreq *ifr)
{
struct {
compat_uptr_t buf;
int used_len;
int total_len;
} compat_priv_data;
hdd_priv_data_t priv_data;
int ret = 0;
/*
* Note that pAdapter and ifr have already been verified by caller,
* and HDD context has also been validated
*/
if (copy_from_user(&compat_priv_data, ifr->ifr_data,
sizeof(compat_priv_data))) {
ret = -EFAULT;
goto exit;
}
priv_data.buf = compat_ptr(compat_priv_data.buf);
priv_data.used_len = compat_priv_data.used_len;
priv_data.total_len = compat_priv_data.total_len;
ret = hdd_hostapd_driver_command(pAdapter, &priv_data);
exit:
return ret;
}
#else /* CONFIG_COMPAT */
static int hdd_hostapd_driver_compat_ioctl(hdd_adapter_t *pAdapter,
struct ifreq *ifr)
{
/* will never be invoked */
return 0;
}
#endif /* CONFIG_COMPAT */
static int hdd_hostapd_driver_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr)
{
hdd_priv_data_t priv_data;
int ret = 0;
/*
* Note that pAdapter and ifr have already been verified by caller,
* and HDD context has also been validated
*/
if (copy_from_user(&priv_data, ifr->ifr_data, sizeof(priv_data))) {
ret = -EFAULT;
} else {
ret = hdd_hostapd_driver_command(pAdapter, &priv_data);
}
return ret;
}
/**
* __hdd_hostapd_ioctl() - hostapd ioctl
* @dev: pointer to net_device
* @ifr: pointer to ifreq structure
* @cmd: command
*
* Return; 0 on success, error number otherwise
*/
static int __hdd_hostapd_ioctl(struct net_device *dev,
struct ifreq *ifr, int cmd)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
int ret;
ENTER();
if (dev != pAdapter->dev) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter/dev inconsistency", __func__);
ret = -ENODEV;
goto exit;
}
if ((!ifr) || (!ifr->ifr_data))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("ifr or ifr->ifr_data is NULL"));
ret = -EINVAL;
goto exit;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (ret) {
ret = -EBUSY;
goto exit;
}
switch (cmd) {
case (SIOCDEVPRIVATE + 1):
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)) && defined(CONFIG_X86_64)
if (in_compat_syscall())
#else
if (is_compat_task())
#endif
ret = hdd_hostapd_driver_compat_ioctl(pAdapter, ifr);
else
ret = hdd_hostapd_driver_ioctl(pAdapter, ifr);
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
__func__, cmd);
ret = -EINVAL;
break;
}
exit:
EXIT();
return ret;
}
/**
* hdd_hostapd_ioctl() - SSR wrapper for __hdd_hostapd_ioctl
* @dev: pointer to net_device
* @ifr: pointer to ifreq structure
* @cmd: command
*
* Return; 0 on success, error number otherwise
*/
static int hdd_hostapd_ioctl(struct net_device *dev,
struct ifreq *ifr, int cmd)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hostapd_ioctl(dev, ifr, cmd);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef QCA_HT_2040_COEX
VOS_STATUS hdd_set_sap_ht2040_mode(hdd_adapter_t *pHostapdAdapter,
tANI_U8 channel_type)
{
eHalStatus halStatus = eHAL_STATUS_FAILURE;
v_PVOID_t hHal = NULL;
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: change HT20/40 mode", __func__);
if (WLAN_HDD_SOFTAP == pHostapdAdapter->device_mode) {
hHal = WLAN_HDD_GET_HAL_CTX(pHostapdAdapter);
if ( NULL == hHal ) {
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: Hal ctx is null", __func__);
return VOS_STATUS_E_FAULT;
}
halStatus = sme_SetHT2040Mode(hHal, pHostapdAdapter->sessionId,
channel_type, eANI_BOOLEAN_TRUE);
if (halStatus == eHAL_STATUS_FAILURE ) {
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to change HT20/40 mode", __func__);
return VOS_STATUS_E_FAILURE;
}
}
return VOS_STATUS_SUCCESS;
}
#endif
#ifdef FEATURE_WLAN_FORCE_SAP_SCC
/**---------------------------------------------------------------------------
\brief hdd_restart_softap() -
Restart SAP on STA channel to support
STA + SAP concurrency.
--------------------------------------------------------------------------*/
void hdd_restart_softap(hdd_context_t *pHddCtx,
hdd_adapter_t *pHostapdAdapter)
{
tHddAvoidFreqList hdd_avoid_freq_list;
/* generate vendor specific event */
vos_mem_zero((void *)&hdd_avoid_freq_list, sizeof(tHddAvoidFreqList));
hdd_avoid_freq_list.avoidFreqRange[0].startFreq =
vos_chan_to_freq(pHostapdAdapter->sessionCtx.ap.operatingChannel);
hdd_avoid_freq_list.avoidFreqRange[0].endFreq =
vos_chan_to_freq(pHostapdAdapter->sessionCtx.ap.operatingChannel);
hdd_avoid_freq_list.avoidFreqRangeCount = 1;
wlan_hdd_send_avoid_freq_event(pHddCtx, &hdd_avoid_freq_list);
}
#endif /* FEATURE_WLAN_FORCE_SAP_SCC */
/**
* __hdd_hostapd_set_mac_address() - set mac address
* @dev: pointer to net_device
* @addr: mac address
*
* This function sets the user specified mac address using
* the command ifconfig wlanX hw ether <mac address>.
*
* Return: 0 on success, error number otherwise
*/
static int __hdd_hostapd_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *psta_mac_addr = addr;
hdd_adapter_t *adapter;
hdd_context_t *hdd_ctx;
int ret = 0;
ENTER();
adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN);
EXIT();
return 0;
}
/**
* hdd_hostapd_set_mac_address() - set mac address
* @dev: pointer to net_device
* @addr: mac address
*
* Return: 0 on success, error number otherwise
*/
static int hdd_hostapd_set_mac_address(struct net_device *dev, void *addr)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_hostapd_set_mac_address(dev, addr);
vos_ssr_unprotect(__func__);
return ret;
}
void hdd_hostapd_inactivity_timer_cb(v_PVOID_t usrDataForCallback)
{
struct net_device *dev = (struct net_device *)usrDataForCallback;
v_BYTE_t we_custom_event[64];
union iwreq_data wrqu;
#ifdef DISABLE_CONCURRENCY_AUTOSAVE
VOS_STATUS vos_status;
hdd_adapter_t *pHostapdAdapter;
hdd_ap_ctx_t *pHddApCtx;
#endif /*DISABLE_CONCURRENCY_AUTOSAVE */
/* event_name space-delimiter driver_module_name */
/* Format of the event is "AUTO-SHUT.indication" " " "module_name" */
char * autoShutEvent = "AUTO-SHUT.indication" " " KBUILD_MODNAME;
int event_len = strlen(autoShutEvent) + 1; /* For the NULL at the end */
ENTER();
#ifdef DISABLE_CONCURRENCY_AUTOSAVE
if (vos_concurrent_open_sessions_running())
{
/*
This timer routine is going to be called only when AP
persona is up.
If there are concurrent sessions running we do not want
to shut down the Bss.Instead we run the timer again so
that if Autosave is enabled next time and other session
was down only then we bring down AP
*/
pHostapdAdapter = netdev_priv(dev);
if ((NULL == pHostapdAdapter) ||
(WLAN_HDD_ADAPTER_MAGIC != pHostapdAdapter->magic))
{
hddLog(LOGE, FL("invalid adapter: %pK"), pHostapdAdapter);
return;
}
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter);
vos_status = vos_timer_start(
&pHddApCtx->hdd_ap_inactivity_timer,
(WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff
* 1000);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
hddLog(LOGE, FL("Failed to init AP inactivity timer"));
}
EXIT();
return;
}
#endif /*DISABLE_CONCURRENCY_AUTOSAVE */
memset(&we_custom_event, '\0', sizeof(we_custom_event));
memcpy(&we_custom_event, autoShutEvent, event_len);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = event_len;
hddLog(LOG1, FL("Shutting down AP interface due to inactivity"));
wireless_send_event(dev, IWEVCUSTOM, &wrqu, (char *)we_custom_event);
EXIT();
}
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
//This function runs in the timer context of hdd_ap_chan_switch_timer.
void hdd_hostapd_chan_switch_cb(v_PVOID_t usrDataForCallback)
{
hdd_adapter_t *pHostapdAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
int ret = 0;
ENTER();
if(usrDataForCallback)
{
pHostapdAdapter = (struct hdd_adapter_s *)usrDataForCallback;
}
else
{
hddLog(LOGE, FL("hdd_hostapd_chan_switch_cb NULL cb pointer!!\n"));
EXIT();
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter);
mutex_lock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock);
if(pHddCtx->ch_switch_ctx.sap_chan_sw_pending)
{
vos_ssr_protect(__func__);
ret = hdd_softap_set_channel_change(pHostapdAdapter->dev, pHddCtx->ch_switch_ctx.def_csa_channel_on_disc);
vos_ssr_unprotect(__func__);
if (ret)
{
hddLog(LOGE, FL("hdd_softap_set_channel_change failed!!"));
}
pHddCtx->ch_switch_ctx.sap_chan_sw_pending = 0;
}
mutex_unlock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock);
EXIT();
}
#endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
static VOS_STATUS
hdd_change_mcc_go_beacon_interval(hdd_adapter_t *pHostapdAdapter)
{
eHalStatus halStatus = eHAL_STATUS_FAILURE;
v_PVOID_t hHal = NULL;
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: UPDATE Beacon Params", __func__);
if(VOS_STA_SAP_MODE == vos_get_conparam ( )){
hHal = WLAN_HDD_GET_HAL_CTX(pHostapdAdapter);
if ( NULL == hHal ){
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: Hal ctx is null", __func__);
return VOS_STATUS_E_FAULT;
}
halStatus = sme_ChangeMCCBeaconInterval(hHal, pHostapdAdapter->sessionId);
if(halStatus == eHAL_STATUS_FAILURE ){
VOS_TRACE( VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to update Beacon Params", __func__);
return VOS_STATUS_E_FAILURE;
}
}
return VOS_STATUS_SUCCESS;
}
void hdd_clear_all_sta(hdd_adapter_t *pHostapdAdapter, v_PVOID_t usrDataForCallback)
{
v_U8_t staId = 0;
struct net_device *dev;
struct tagCsrDelStaParams delStaParams;
dev = (struct net_device *)usrDataForCallback;
hddLog(LOGE, FL("Clearing all the STA entry...."));
for (staId = 0; staId < WLAN_MAX_STA_COUNT; staId++)
{
if ( pHostapdAdapter->aStaInfo[staId].isUsed &&
( staId != (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uBCStaId))
{
WLANSAP_PopulateDelStaParams(&pHostapdAdapter->aStaInfo[staId].macAddrSTA.bytes[0],
eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
(SIR_MAC_MGMT_DISASSOC >> 4),
&delStaParams);
//Disconnect all the stations
hdd_softap_sta_disassoc(pHostapdAdapter, &delStaParams);
}
}
}
static int hdd_stop_bss_link(hdd_adapter_t *pHostapdAdapter,
v_PVOID_t usrDataForCallback)
{
struct net_device *dev;
hdd_context_t *pHddCtx = NULL;
VOS_STATUS status = VOS_STATUS_SUCCESS;
dev = (struct net_device *)usrDataForCallback;
ENTER();
pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return status;
if(test_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags))
{
#ifdef WLAN_FEATURE_MBSSID
status = WLANSAP_StopBss(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter));
#else
status = WLANSAP_StopBss((WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext);
#endif
if (VOS_IS_STATUS_SUCCESS(status))
hddLog(LOGE, FL("Deleting SAP/P2P link!!!!!!"));
clear_bit(SOFTAP_BSS_STARTED, &pHostapdAdapter->event_flags);
wlan_hdd_decr_active_session(pHddCtx, pHostapdAdapter->device_mode);
}
if (pHddCtx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss);
EXIT();
return (status == VOS_STATUS_SUCCESS) ? 0 : -EBUSY;
}
#ifdef SAP_AUTH_OFFLOAD
void hdd_set_sap_auth_offload(hdd_adapter_t *pHostapdAdapter,
bool enabled)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pHostapdAdapter);
struct tSirSapOffloadInfo *sap_offload_info = NULL;
/* Prepare the request to send to SME */
sap_offload_info = vos_mem_malloc(sizeof(*sap_offload_info));
if (NULL == sap_offload_info) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: could not allocate tSirSapOffloadInfo!", __func__);
return;
}
vos_mem_zero(sap_offload_info, sizeof(*sap_offload_info));
sap_offload_info->vdev_id = pHostapdAdapter->sessionId;
sap_offload_info->sap_auth_offload_enable =
pHddCtx->cfg_ini->enable_sap_auth_offload && enabled;
sap_offload_info->sap_auth_offload_sec_type =
pHddCtx->cfg_ini->sap_auth_offload_sec_type;
sap_offload_info->key_len =
strlen(pHddCtx->cfg_ini->sap_auth_offload_key);
if (sap_offload_info->sap_auth_offload_enable) {
if (sap_offload_info->key_len < 8 ||
sap_offload_info->key_len > WLAN_PSK_STRING_LENGTH) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: invalid key length(%d) of WPA security!", __func__,
sap_offload_info->key_len);
goto end;
}
}
vos_mem_copy(sap_offload_info->key,
pHddCtx->cfg_ini->sap_auth_offload_key,
sap_offload_info->key_len);
if (eHAL_STATUS_SUCCESS !=
sme_set_sap_auth_offload(pHddCtx->hHal, sap_offload_info)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_set_sap_auth_offload fail!", __func__);
goto end;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: sme_set_sap_auth_offload successfully!", __func__);
end:
vos_mem_free(sap_offload_info);
return;
}
/**
* hdd_set_client_block_info - get client block info from ini file
* @padapter: hdd adapter pointer
*
* This function reads client block related info from ini file, these
* configurations will be sent to fw through wmi.
*
* Return: 0 on success, otherwise error value
*/
int hdd_set_client_block_info(hdd_adapter_t *padapter)
{
hdd_context_t *phddctx = WLAN_HDD_GET_CTX(padapter);
struct sblock_info client_block_info;
eHalStatus status;
/* prepare the request to send to SME */
client_block_info.vdev_id = padapter->sessionId;
client_block_info.reconnect_cnt =
phddctx->cfg_ini->connect_fail_count;
client_block_info.con_fail_duration =
phddctx->cfg_ini->connect_fail_duration;
client_block_info.block_duration =
phddctx->cfg_ini->connect_block_duration;
status = sme_set_client_block_info(phddctx->hHal, &client_block_info);
if (eHAL_STATUS_FAILURE == status) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_set_client_block_info!", __func__);
return -EIO;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: sme_set_client_block_info success!", __func__);
return 0;
}
#endif /* SAP_AUTH_OFFLOAD */
/**
* hdd_issue_stored_joinreq() - This function will trigger stations's
* cached connect request to proceed.
* @hdd_ctx: pointer to hdd context.
* @sta_adapter: pointer to station adapter.
*
* This function will call SME to release station's stored/cached connect
* request to proceed.
*
* Return: none.
*/
static void hdd_issue_stored_joinreq(hdd_adapter_t *sta_adapter,
hdd_context_t *hdd_ctx)
{
tHalHandle hal_handle;
uint32_t roam_id = 0;
if (NULL == sta_adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Invalid station adapter, ignore issueing join req"));
return;
}
hal_handle = WLAN_HDD_GET_HAL_CTX(sta_adapter);
if (true == hdd_is_sta_connection_pending(hdd_ctx)) {
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_ISSUE_JOIN_REQ,
sta_adapter->sessionId, roam_id));
if (VOS_STATUS_SUCCESS !=
sme_issue_stored_joinreq(hal_handle,
&roam_id,
sta_adapter->sessionId)) {
/* change back to NotAssociated */
hdd_connSetConnectionState(sta_adapter,
eConnectionState_NotConnected);
}
hdd_change_sta_conn_pending_status(hdd_ctx, false);
}
}
/**
* hdd_update_chandef() - Function to update channel width and center freq
* @hostapd_adapter: hostapd adapter
* @chandef: cfg80211 chan def
* @cb_mode: chan offset
*
* This function will be called to update channel width and center freq
*
* Return: None
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) || defined(WITH_BACKPORTS)
static inline void
hdd_update_chandef(hdd_adapter_t *hostapd_adapter,
struct cfg80211_chan_def *chandef,
ePhyChanBondState cb_mode)
{
uint16_t ch_width;
hdd_ap_ctx_t *phdd_ap_ctx;
uint8_t center_chan, chan;
phdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hostapd_adapter);
ch_width = phdd_ap_ctx->sapConfig.acs_cfg.ch_width;
switch (ch_width) {
case eHT_CHANNEL_WIDTH_20MHZ:
case eHT_CHANNEL_WIDTH_40MHZ:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"ch_width %d, won't update", ch_width);
break;
case eHT_CHANNEL_WIDTH_80MHZ:
chan = vos_freq_to_chan(chandef->chan->center_freq);
chandef->width = NL80211_CHAN_WIDTH_80;
switch (cb_mode) {
case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED:
case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW:
center_chan = chan + 2;
break;
case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW:
center_chan = chan + 6;
break;
case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH:
case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED:
center_chan = chan - 2;
break;
case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH:
center_chan = chan - 6;
break;
default:
center_chan = chan;
break;
}
chandef->center_freq1 = vos_chan_to_freq(center_chan);
break;
case eHT_CHANNEL_WIDTH_160MHZ:
default:
/* Todo, please add related codes if support 160MHZ or others */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"unsupport ch_width %d", ch_width);
break;
}
}
#else
static inline void
hdd_update_chandef(hdd_adapter_t *hostapd_adapter,
struct cfg80211_chan_def *chandef,
ePhyChanBondState cb_mode)
{
}
#endif
/**
* hdd_chan_change_notify() - Function to notify hostapd about channel change
* @hostapd_adapter hostapd adapter
* @dev: Net device structure
* @oper_chan: New operating channel
*
* This function is used to notify hostapd about the channel change
*
* Return: Success on intimating userspace
*
*/
VOS_STATUS hdd_chan_change_notify(hdd_adapter_t *hostapd_adapter,
struct net_device *dev,
uint8_t oper_chan)
{
struct ieee80211_channel *chan;
struct cfg80211_chan_def chandef;
enum nl80211_channel_type channel_type;
eCsrPhyMode phy_mode;
ePhyChanBondState cb_mode;
uint32_t freq;
tHalHandle hal = WLAN_HDD_GET_HAL_CTX(hostapd_adapter);
if (NULL == hal) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: hal is NULL", __func__);
return VOS_STATUS_E_FAILURE;
}
freq = vos_chan_to_freq(oper_chan);
chan = ieee80211_get_channel(hostapd_adapter->wdev.wiphy, freq);
if (!chan) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid input frequency for channel conversion",
__func__);
return VOS_STATUS_E_FAILURE;
}
#ifdef WLAN_FEATURE_MBSSID
phy_mode = wlansap_get_phymode(WLAN_HDD_GET_SAP_CTX_PTR(hostapd_adapter));
#else
phy_mode = wlansap_get_phymode(
(WLAN_HDD_GET_CTX(hostapd_adapter))->pvosContext);
#endif
if (oper_chan <= 14)
cb_mode = sme_GetCBPhyStateFromCBIniValue(
sme_GetChannelBondingMode24G(hal));
else
cb_mode = sme_GetCBPhyStateFromCBIniValue(
sme_GetChannelBondingMode5G(hal));
switch (phy_mode) {
case eCSR_DOT11_MODE_11n:
case eCSR_DOT11_MODE_11n_ONLY:
case eCSR_DOT11_MODE_11ac:
case eCSR_DOT11_MODE_11ac_ONLY:
if (cb_mode == PHY_SINGLE_CHANNEL_CENTERED)
channel_type = NL80211_CHAN_HT20;
else if (cb_mode == PHY_DOUBLE_CHANNEL_HIGH_PRIMARY)
channel_type = NL80211_CHAN_HT40MINUS;
else if (cb_mode == PHY_DOUBLE_CHANNEL_LOW_PRIMARY)
channel_type = NL80211_CHAN_HT40PLUS;
else
channel_type = NL80211_CHAN_HT40PLUS;
break;
default:
channel_type = NL80211_CHAN_NO_HT;
break;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: phy_mode %d cb_mode %d chann_type %d oper_chan %d",
__func__, phy_mode, cb_mode, channel_type, oper_chan);
cfg80211_chandef_create(&chandef, chan, channel_type);
if ((phy_mode == eCSR_DOT11_MODE_11ac) ||
(phy_mode == eCSR_DOT11_MODE_11ac_ONLY))
hdd_update_chandef(hostapd_adapter, &chandef, cb_mode);
cfg80211_ch_switch_notify(dev, &chandef);
return VOS_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
VOS_STATUS hdd_send_sap_event(struct net_device *dev,
sta_sap_notifications event,
struct wlan_sap_csa_info csa_info,
struct wireless_dev *wdev)
{
uint32_t freq = 0, ret;
hdd_wlan_get_freq(csa_info.sta_channel, &freq);
hddLog(LOG1, FL(" Set Freq %d Chan= %d"), freq, csa_info.sta_channel );
vos_ssr_protect(__func__);
ret = hdd_softap_set_channel_change(dev, csa_info.sta_channel);
vos_ssr_unprotect(__func__);
return ret;
}
VOS_STATUS hdd_sta_state_sap_notify(hdd_context_t *hdd_context,
sta_sap_notifications event,
struct wlan_sap_csa_info csa_info)
{
/* Get the HostApd Adapter. If present proceed further.
* Check the current state of SAP. If its in active state, get the channel in which it is running.
* Verify the channel and band. Based on the event type, take a decision.
* If it is a disconnection event and SAP is running in 2.4 band channel, no action should be taken.
* If its a connection event and SAP needs to do a CSA to the HomeAP channel.
*/
hdd_adapter_t *pHostapdAdapter = NULL;
hdd_ap_ctx_t *pHddApCtx = NULL;
hdd_hostapd_state_t *pHostapdState = NULL;
VOS_STATUS status = VOS_STATUS_SUCCESS;
tsap_Config_t *sap_config;
uint32_t ret = 0;
hddLog(LOGE, FL("%s Entry event = %d channel = %d"),
__func__, event, csa_info.sta_channel);
if (!hdd_context) {
hddLog(LOGE, FL("HDD context is NULL"));
return VOS_STATUS_E_FAILURE;
}
ret = wlan_hdd_validate_context(hdd_context);
if (ret != 0) {
hddLog(LOGE, FL("%s Failed in hdd_validate_context ret=%d"), __func__, ret);
return ret;
}
/*Get the Adapter of SAP*/
pHostapdAdapter = hdd_get_adapter(hdd_context, WLAN_HDD_SOFTAP);
if(!pHostapdAdapter)
{
hddLog(LOGE, FL("Hostapd adapter context is NULL"));
return VOS_STATUS_E_FAILURE;
}
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter);
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter);
/*Verify the state*/
if(pHostapdState->vosStatus != VOS_STATUS_SUCCESS ||
pHostapdState->bssState != BSS_START)
{
hddLog(LOGE, FL("Invalid HostApd State vosStatus=%d bssState=%d"),
pHostapdState->vosStatus, pHostapdState->bssState);
return VOS_STATUS_E_FAILURE;
}
switch(event)
{
case STA_NOTIFY_DISCONNECTED:
{
/* check for the operating channel
* If operating in 2.4, just ignore and return
* else start ACS & find the strongest signal channel and do initiate CSA to that channel.
*/
if((pHddApCtx->operatingChannel >= 1 && pHddApCtx->operatingChannel <= 14))
{
hddLog(LOGE, FL("Hostapd is operating in 2.4Band Channel=%d, Avoid channel switch"),
pHddApCtx->operatingChannel);
}
else
{
sap_config = &((WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->sapConfig);
if (VOS_IS_DFS_CH(pHddApCtx->operatingChannel) &&
( VOS_IS_DFS_CH(sap_config->channel) ||
(sap_config->channel == AUTO_CHANNEL_SELECT) )){
hddLog(LOGE, FL("SAP CUR CH %d(DFS) Hostapd Conf CH=%d(%s) Switch to CH %d"),
pHddApCtx->operatingChannel, pHddApCtx->operatingChannel,
(sap_config->channel == AUTO_CHANNEL_SELECT)?"AUTO":"DFS", 36);
hdd_context->ch_switch_ctx.def_csa_channel_on_disc = 36;
}else if (VOS_IS_DFS_CH(pHddApCtx->operatingChannel) &&
!VOS_IS_DFS_CH(sap_config->channel)){
hddLog(LOGE, FL("SAP CUR CH %d(DFS) Hostapd Conf CH=%d(Non-DFS) Switch to %d"),
pHddApCtx->operatingChannel, sap_config->channel, sap_config->channel);
hdd_context->ch_switch_ctx.def_csa_channel_on_disc = sap_config->channel; //channel from the hostapd
}else{
hddLog(LOGE, FL("SAP is operating in 5Ghz Band Non DFS Channel=%d, Avoid channel switch"),
pHddApCtx->operatingChannel);
return status;
}
mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
hdd_context->ch_switch_ctx.sap_chan_sw_pending = 1;
mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
//Set the timer to initiate channel switch
if(hdd_context->ch_switch_ctx.chan_sw_timer_initialized == VOS_TRUE)
{
status = vos_timer_start(&hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer, 10000);
if(!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(LOGE, FL("Failed to start AP channel switch timer!!"));
break;
}
}
}
}
break;
case STA_NOTIFY_CONNECTED:
{
//stop the channel switch timer first
if (hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer.state == VOS_TIMER_STATE_RUNNING)
{
status = vos_timer_stop(&hdd_context->ch_switch_ctx.hdd_ap_chan_switch_timer);
if(!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(LOGE, FL("Failed to stop AP channel switch timer!!"));
break;
}
}
if(pHddApCtx->operatingChannel != csa_info.sta_channel)
{
mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
hddLog(LOGE, FL("Switching Hostapd to Station channel %d"), csa_info.sta_channel);
status = hdd_send_sap_event(pHostapdAdapter->dev,
event,
csa_info,
&pHostapdAdapter->wdev);
if(!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(LOGE, FL("Failed to send channel switch event!!"));
mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
break;
}
hdd_context->ch_switch_ctx.sap_chan_sw_pending = 0;
mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
}
else
{
hddLog(LOGE, FL("Hostapd and Sta are operating in same channel : %d\n"),
pHddApCtx->operatingChannel);
}
}
break;
case STA_NOTIFY_CSA:
{
mutex_lock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
if(pHddApCtx->operatingChannel != csa_info.sta_channel)
{
if(!(hdd_context->ch_switch_ctx.is_ch_sw_through_sta_csa &&
hdd_context->ch_switch_ctx.csa_to_channel == csa_info.sta_channel))
{
hdd_context->ch_switch_ctx.is_ch_sw_through_sta_csa = VOS_TRUE;
hddLog(LOGE, FL("Switching Hostapd to Station channel %d"), csa_info.sta_channel);
status = hdd_send_sap_event(pHostapdAdapter->dev,
event,
csa_info,
&pHostapdAdapter->wdev);
if(!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(LOGE, FL("Failed to send channel switch event!!"));
mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
break;
}
hdd_context->ch_switch_ctx.csa_to_channel = csa_info.sta_channel;
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : CSA Sta interface for Channel %d is already notified",
__func__, csa_info.sta_channel);
}
}
else
{
hddLog(LOGE, FL("Hostapd and Sta are operating in same channel : %d\n"),
pHddApCtx->operatingChannel);
}
mutex_unlock(&hdd_context->ch_switch_ctx.sap_ch_sw_lock);
}
break;
default:
{
hddLog(LOGE, FL("%s Invalid event %d"), __func__, event);
}
break;
}
hddLog(LOGE, FL("%s Exit ret = %d"), __func__, status);
return status;
}
#endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
/**
* hdd_send_radar_event() - Function to send radar events to user space
* @hdd_context: HDD context
* @event: Type of radar event
* @dfs_info: Structure containing DFS channel and country
* @wdev: Wireless device structure
*
* This function is used to send radar events such as CAC start, CAC
* end etc., to userspace
*
* Return: Success on sending notifying userspace
*
*/
VOS_STATUS hdd_send_radar_event(hdd_context_t *hdd_context,
eSapHddEvent event,
struct wlan_dfs_info dfs_info,
struct wireless_dev *wdev)
{
struct sk_buff *vendor_event;
enum qca_nl80211_vendor_subcmds_index index;
uint32_t freq, ret;
uint32_t data_size;
if (!hdd_context) {
hddLog(LOGE, FL("HDD context is NULL"));
return VOS_STATUS_E_FAILURE;
}
freq = vos_chan_to_freq(dfs_info.channel);
switch (event) {
case eSAP_DFS_CAC_START:
index =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED_INDEX;
data_size = sizeof(uint32_t);
break;
case eSAP_DFS_CAC_END:
index =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED_INDEX;
data_size = sizeof(uint32_t);
break;
case eSAP_DFS_RADAR_DETECT:
index =
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED_INDEX;
data_size = sizeof(uint32_t);
break;
default:
return VOS_STATUS_E_FAILURE;
}
vendor_event = cfg80211_vendor_event_alloc(hdd_context->wiphy,
wdev,
data_size + NLMSG_HDRLEN,
index,
GFP_KERNEL);
if (!vendor_event) {
hddLog(LOGE,
FL("cfg80211_vendor_event_alloc failed for %d"), index);
return VOS_STATUS_E_FAILURE;
}
ret = nla_put_u32(vendor_event, NL80211_ATTR_WIPHY_FREQ, freq);
if (ret) {
hddLog(LOGE, FL("NL80211_ATTR_WIPHY_FREQ put fail"));
kfree_skb(vendor_event);
return VOS_STATUS_E_FAILURE;
}
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
return VOS_STATUS_SUCCESS;
}
#ifdef CONFIG_CNSS
static VOS_STATUS hdd_wlan_get_dfs_nol(void *pdfs_list, u16 sdfs_list)
{
int ret;
/* get the dfs nol from cnss */
ret = vos_wlan_get_dfs_nol(pdfs_list, sdfs_list);
if (ret > 0) {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Get %d bytes of dfs nol from cnss",
__func__, ret);
return VOS_STATUS_SUCCESS;
} else {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: No dfs nol entry in CNSS, ret: %d",
__func__, ret);
return VOS_STATUS_E_FAULT;
}
}
#else
static VOS_STATUS hdd_wlan_get_dfs_nol(void *pdfs_list, u16 sdfs_list)
{
return VOS_STATUS_E_FAILURE;
}
#endif
#ifdef CONFIG_CNSS
static VOS_STATUS hdd_wlan_set_dfs_nol(const void *pdfs_list, u16 sdfs_list)
{
int ret;
/* set the dfs nol from cnss */
ret = vos_wlan_set_dfs_nol(pdfs_list, sdfs_list);
if (ret) {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Failed to set dfs nol - ret: %d",
__func__, ret);
} else {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Set %d bytes dfs nol to cnss",
__func__, sdfs_list);
}
return VOS_STATUS_SUCCESS;
}
#else
static VOS_STATUS hdd_wlan_set_dfs_nol(const void *pdfs_list, u16 sdfs_list)
{
return VOS_STATUS_E_FAILURE;
}
#endif
#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
/**
* hdd_handle_acs_scan_event() - handle acs scan event for SAP
* @sap_event: tpSap_Event
* @adapter: hdd_adapter_t for SAP
*
* The function is to handle the eSAP_ACS_SCAN_SUCCESS_EVENT event.
* It will update scan result to cfg80211 and start a timer to flush the
* cached acs scan result.
*
* Return: VOS_STATUS_SUCCESS on success,
other value on failure
*/
static VOS_STATUS hdd_handle_acs_scan_event(tpSap_Event sap_event,
hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx;
struct tsap_acs_scan_complete_event *comp_evt;
VOS_STATUS vos_status;
int chan_list_size;
hdd_ctx = (hdd_context_t*)(adapter->pHddCtx);
if (!hdd_ctx) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is null"));
return VOS_STATUS_E_FAILURE;
}
comp_evt = &sap_event->sapevt.sap_acs_scan_comp;
hdd_ctx->skip_acs_scan_status = eSAP_SKIP_ACS_SCAN;
spin_lock(&hdd_ctx->acs_skip_lock);
vos_mem_free(hdd_ctx->last_acs_channel_list);
hdd_ctx->last_acs_channel_list = NULL;
hdd_ctx->num_of_channels = 0;
/* cache the previous ACS scan channel list .
* If the following OBSS scan chan list is covered by ACS chan list,
* we can skip OBSS Scan to save SAP starting total time.
*/
if (comp_evt->num_of_channels && comp_evt->channellist) {
chan_list_size = comp_evt->num_of_channels *
sizeof(comp_evt->channellist[0]);
hdd_ctx->last_acs_channel_list = vos_mem_malloc(
chan_list_size);
if (hdd_ctx->last_acs_channel_list) {
vos_mem_copy(hdd_ctx->last_acs_channel_list,
comp_evt->channellist,
chan_list_size);
hdd_ctx->num_of_channels = comp_evt->num_of_channels;
}
}
spin_unlock(&hdd_ctx->acs_skip_lock);
/* Update ACS scan result to cfg80211. Then OBSS scan can reuse the
* scan result.
*/
if (wlan_hdd_cfg80211_update_bss(hdd_ctx->wiphy, adapter))
hddLog(VOS_TRACE_LEVEL_INFO, FL("NO SCAN result"));
hddLog(LOG1, FL("Reusing Last ACS scan result for %d sec"),
ACS_SCAN_EXPIRY_TIMEOUT_S);
vos_timer_stop( &hdd_ctx->skip_acs_scan_timer);
vos_status = vos_timer_start( &hdd_ctx->skip_acs_scan_timer,
ACS_SCAN_EXPIRY_TIMEOUT_S * 1000);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to start ACS scan expiry timer"));
return VOS_STATUS_SUCCESS;
}
#else
static VOS_STATUS hdd_handle_acs_scan_event(tpSap_Event sap_event,
hdd_adapter_t *adapter)
{
return VOS_STATUS_SUCCESS;
}
#endif
/**
* get_max_rate_vht() - calculate max rate for VHT mode
* @nss: num of streams
* @ch_width: channel width
* @sgi: short gi
* @vht_mcs_map: vht mcs map
*
* This function calculate max rate for VHT mode
*
* Return: max rate
*/
#ifdef WLAN_FEATURE_11AC
static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map)
{
struct index_vht_data_rate_type *supported_vht_mcs_rate;
enum data_rate_11ac_max_mcs vht_max_mcs;
int maxrate = 0;
int maxidx = 0;
if (nss == 1) {
supported_vht_mcs_rate = supported_vht_mcs_rate_nss1;
} else if (nss == 2) {
supported_vht_mcs_rate = supported_vht_mcs_rate_nss2;
} else {
/* Not Supported */
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: nss %d not supported\n",
__func__, nss);
return maxrate;
}
vht_max_mcs =
(enum data_rate_11ac_max_mcs)
vht_mcs_map & DATA_RATE_11AC_MCS_MASK;
if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_7) {
maxidx = 7;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_8) {
maxidx = 8;
} else if (vht_max_mcs == DATA_RATE_11AC_MAX_MCS_9) {
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ)
/* MCS9 is not valid for VHT20 when nss=1,2 */
maxidx = 8;
else
maxidx = 9;
} else {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: vht mcs map %x not supported\n",
__func__,
vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
return maxrate;
}
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT20_rate[sgi];
} else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT40_rate[sgi];
} else if (ch_width == eHT_CHANNEL_WIDTH_80MHZ) {
maxrate =
supported_vht_mcs_rate[maxidx].supported_VHT80_rate[sgi];
} else {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: ch_width %d not supported\n",
__func__, ch_width);
return maxrate;
}
return maxrate;
}
#else
static int get_max_rate_vht(int nss, int ch_width, int sgi, int vht_mcs_map)
{
return 0;
}
#endif
/**
* calculate_max_phy_rate() - calcuate maximum phy rate (kbps)
* @mode: phymode: Legacy, 11a/b/g, HT, VHT
* @nss: num of stream (maximum num is 2)
* @ch_width: channel width
* @sgi: short gi enabled or not
* @supp_idx: max supported idx
* @ext_idx: max extended idx
* @ht_mcs_idx: max mcs index for HT
* @vht_mcs_map: mcs map for VHT
*
* return: maximum phy rate in kbps
* */
static int calcuate_max_phy_rate(int mode, int nss, int ch_width,
int sgi, int supp_idx, int ext_idx, int ht_mcs_idx,
int vht_mcs_map)
{
struct index_data_rate_type *supported_mcs_rate;
int maxidx = 12; /*default 6M mode*/
int maxrate = 0, tmprate = 0;
int i;
/* check supported rates */
if (supp_idx != 0xff && maxidx < supp_idx)
maxidx = supp_idx;
/* check extended rates */
if (ext_idx != 0xff && maxidx < ext_idx)
maxidx = ext_idx;
for (i = 0;
i < sizeof(supported_data_rate)/sizeof(supported_data_rate[0]);
i++) {
if (supported_data_rate[i].beacon_rate_index == maxidx)
maxrate = supported_data_rate[i].supported_rate[0];
}
if (mode == SIR_SME_PHY_MODE_HT) {
/* check for HT Mode */
maxidx = ht_mcs_idx;
if (nss == 1) {
supported_mcs_rate = supported_mcs_rate_nss1;
} else if (nss == 2) {
supported_mcs_rate = supported_mcs_rate_nss2;
} else {
/* Not Supported */
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: nss %d not supported\n",
__func__, nss);
return maxrate;
}
if (ch_width == eHT_CHANNEL_WIDTH_20MHZ) {
tmprate =
supported_mcs_rate[maxidx].supported_rate[0];
if (sgi)
tmprate =
supported_mcs_rate[maxidx].supported_rate[2];
} else if (ch_width == eHT_CHANNEL_WIDTH_40MHZ) {
tmprate =
supported_mcs_rate[maxidx].supported_rate[1];
if (sgi)
tmprate =
supported_mcs_rate[maxidx].supported_rate[3];
} else {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: invalid mode %d ch_width %d\n",
__func__, mode, ch_width);
return maxrate;
}
if (maxrate < tmprate)
maxrate = tmprate;
}
if (mode == SIR_SME_PHY_MODE_VHT) {
/* check for VHT Mode */
tmprate = get_max_rate_vht(nss, ch_width, sgi, vht_mcs_map);
if (maxrate < tmprate)
maxrate = tmprate;
}
return maxrate;
}
static void hdd_fill_station_info(hdd_station_info_t *stainfo,
tSap_StationAssocReassocCompleteEvent *event)
{
stainfo->nss = event->chan_info.nss;
stainfo->rate_flags = event->chan_info.rate_flags;
stainfo->sub20_dynamic_channelwidth =
event->chan_info.sub20_channelwidth;
stainfo->ampdu = event->ampdu;
stainfo->sgi_enable = event->sgi_enable;
stainfo->tx_stbc = event->tx_stbc;
stainfo->rx_stbc = event->rx_stbc;
stainfo->ch_width = event->ch_width;
stainfo->mode = event->mode;
stainfo->max_supp_idx = event->max_supp_idx;
stainfo->max_ext_idx = event->max_ext_idx;
stainfo->max_mcs_idx = event->max_mcs_idx;
stainfo->rx_mcs_map = event->rx_mcs_map;
stainfo->tx_mcs_map = event->tx_mcs_map;
stainfo->assoc_ts = vos_system_ticks();
stainfo->max_phy_rate =
calcuate_max_phy_rate(stainfo->mode,
stainfo->nss,
stainfo->ch_width,
stainfo->sgi_enable,
stainfo->max_supp_idx,
stainfo->max_ext_idx,
stainfo->max_mcs_idx,
stainfo->rx_mcs_map);
/* expect max_phy_rate report in kbps */
stainfo->max_phy_rate *= 100;
hddLog(VOS_TRACE_LEVEL_INFO,
FL("cap %d %d %d %d %d %d %d %d %d %x %d"),
stainfo->ampdu,
stainfo->sgi_enable,
stainfo->tx_stbc,
stainfo->rx_stbc,
stainfo->isQosEnabled,
stainfo->ch_width,
stainfo->mode,
event->wmmEnabled,
event->chan_info.nss,
event->chan_info.rate_flags,
stainfo->max_phy_rate);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("rate info %d %d %d %d %d"),
stainfo->max_supp_idx,
stainfo->max_ext_idx,
stainfo->max_mcs_idx,
stainfo->rx_mcs_map,
stainfo->tx_mcs_map);
}
VOS_STATUS hdd_hostapd_SAPEventCB( tpSap_Event pSapEvent, v_PVOID_t usrDataForCallback)
{
hdd_adapter_t *pHostapdAdapter;
hdd_ap_ctx_t *pHddApCtx;
hdd_hostapd_state_t *pHostapdState;
struct net_device *dev;
eSapHddEvent sapEvent;
union iwreq_data wrqu;
v_BYTE_t *we_custom_event_generic = NULL;
int we_event = 0;
int i = 0;
v_U8_t staId;
VOS_STATUS vos_status;
v_BOOL_t bWPSState;
v_BOOL_t bAuthRequired = TRUE;
tpSap_AssocMacAddr pAssocStasArray = NULL;
char unknownSTAEvent[IW_CUSTOM_MAX+1];
char maxAssocExceededEvent[IW_CUSTOM_MAX+1];
v_BYTE_t we_custom_start_event[64];
char *startBssEvent;
hdd_context_t *pHddCtx;
hdd_scaninfo_t *pScanInfo = NULL;
struct iw_michaelmicfailure msg;
v_U8_t ignoreCAC = 0;
hdd_config_t *cfg = NULL;
struct wlan_dfs_info dfs_info;
v_U8_t cc_len = WLAN_SVC_COUNTRY_CODE_LEN;
hdd_adapter_t *con_sap_adapter;
tSap_StationAssocReassocCompleteEvent *event;
VOS_STATUS status = VOS_STATUS_SUCCESS;
dev = (struct net_device *)usrDataForCallback;
if (!dev)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: usrDataForCallback is null", __func__);
return VOS_STATUS_E_FAILURE;
}
pHostapdAdapter = netdev_priv(dev);
if ((NULL == pHostapdAdapter) ||
(WLAN_HDD_ADAPTER_MAGIC != pHostapdAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid adapter or adapter has invalid magic");
return VOS_STATUS_E_FAILURE;
}
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pHostapdAdapter);
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter);
if (!pSapEvent)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pSapEvent is null", __func__);
return VOS_STATUS_E_FAILURE;
}
sapEvent = pSapEvent->sapHddEventCode;
memset(&wrqu, '\0', sizeof(wrqu));
pHddCtx = (hdd_context_t*)(pHostapdAdapter->pHddCtx);
if (!pHddCtx) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD context is null"));
return VOS_STATUS_E_FAILURE;
}
cfg = pHddCtx->cfg_ini;
if (!cfg) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD config is null"));
return VOS_STATUS_E_FAILURE;
}
dfs_info.channel = pHddApCtx->operatingChannel;
sme_GetCountryCode(pHddCtx->hHal, dfs_info.country_code, &cc_len);
switch(sapEvent)
{
case eSAP_START_BSS_EVENT :
hddLog(LOG1, FL("BSS configured status = %s, channel = %u, bc sta Id = %d"),
pSapEvent->sapevt.sapStartBssCompleteEvent.status ? "eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS",
pSapEvent->sapevt.sapStartBssCompleteEvent.operatingChannel,
pSapEvent->sapevt.sapStartBssCompleteEvent.staId);
pHostapdAdapter->sessionId =
pSapEvent->sapevt.sapStartBssCompleteEvent.sessionId;
pHostapdState->vosStatus = pSapEvent->sapevt.sapStartBssCompleteEvent.status;
vos_status = vos_event_set(&pHostapdState->vosEvent);
if (!VOS_IS_STATUS_SUCCESS(vos_status) || pHostapdState->vosStatus)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: startbss event failed!!"));
goto stopbss;
}
else
{
#ifdef FEATURE_WLAN_CH_AVOID
sme_ChAvoidUpdateReq(pHddCtx->hHal);
#endif /* FEATURE_WLAN_CH_AVOID */
pHddApCtx->uBCStaId = pSapEvent->sapevt.sapStartBssCompleteEvent.staId;
#ifdef QCA_LL_TX_FLOW_CT
if (pHostapdAdapter->tx_flow_timer_initialized == VOS_FALSE)
{
vos_timer_init(&pHostapdAdapter->tx_flow_control_timer,
VOS_TIMER_TYPE_SW,
hdd_tx_resume_timer_expired_handler,
pHostapdAdapter);
pHostapdAdapter->tx_flow_timer_initialized = VOS_TRUE;
}
WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext,
hdd_tx_resume_cb,
pHostapdAdapter->sessionId,
(void *)pHostapdAdapter);
#endif
//@@@ need wep logic here to set privacy bit
vos_status = hdd_softap_Register_BC_STA(pHostapdAdapter, pHddApCtx->uPrivacy);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
hddLog(LOGW, FL("Failed to register BC STA %d"), vos_status);
hdd_stop_bss_link(pHostapdAdapter, usrDataForCallback);
}
}
#ifdef IPA_OFFLOAD
if (hdd_ipa_is_enabled(pHddCtx))
{
status = hdd_ipa_wlan_evt(pHostapdAdapter, pHddApCtx->uBCStaId,
WLAN_AP_CONNECT, pHostapdAdapter->dev->dev_addr);
if (status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: WLAN_AP_CONNECT event failed!!"));
goto stopbss;
}
}
#endif
if (0 != (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff)
{
// AP Inactivity timer init and start
vos_status = vos_timer_init( &pHddApCtx->hdd_ap_inactivity_timer, VOS_TIMER_TYPE_SW,
hdd_hostapd_inactivity_timer_cb, (v_PVOID_t)dev );
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to init AP inactivity timer"));
vos_status = vos_timer_start( &pHddApCtx->hdd_ap_inactivity_timer, (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff * 1000);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to init AP inactivity timer"));
}
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_TRUE);
#endif
pHddApCtx->operatingChannel = pSapEvent->sapevt.sapStartBssCompleteEvent.operatingChannel;
hdd_hostapd_channel_prevent_suspend(pHostapdAdapter,
pHddApCtx->operatingChannel);
pHostapdState->bssState = BSS_START;
/* Set default key index */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: default key index %hu", __func__,
pHddApCtx->wep_def_key_idx);
sme_roam_set_default_key_index(
WLAN_HDD_GET_HAL_CTX(pHostapdAdapter),
pHostapdAdapter->sessionId,
pHddApCtx->wep_def_key_idx);
//Set group key / WEP key every time when BSS is restarted
if( pHddApCtx->groupKey.keyLength )
{
status = WLANSAP_SetKeySta(
#ifdef WLAN_FEATURE_MBSSID
WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter),
#else
(WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext,
#endif
&pHddApCtx->groupKey);
if (!VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_SetKeySta failed", __func__);
}
}
else
{
for ( i = 0; i < CSR_MAX_NUM_KEY; i++ )
{
if ( !pHddApCtx->wepKey[i].keyLength )
continue;
status = WLANSAP_SetKeySta(
#ifdef WLAN_FEATURE_MBSSID
WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter),
#else
(WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext,
#endif
&pHddApCtx->wepKey[i]);
if (!VOS_IS_STATUS_SUCCESS(status))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_SetKeySta failed idx %d", __func__, i);
}
}
}
SPIN_LOCK_BH(&pHddCtx->dfs_lock);
pHddCtx->dfs_radar_found = VOS_FALSE;
SPIN_UNLOCK_BH(&pHddCtx->dfs_lock);
WLANSAP_Get_Dfs_Ignore_CAC(pHddCtx->hHal, &ignoreCAC);
if ((NV_CHANNEL_DFS !=
vos_nv_getChannelEnabledState(pHddApCtx->operatingChannel))
|| ignoreCAC
|| pHddCtx->dev_dfs_cac_status == DFS_CAC_ALREADY_DONE)
{
pHddApCtx->dfs_cac_block_tx = VOS_FALSE;
} else {
/*
* DFS requirement: Do not transmit during CAC.
* This flag will be reset when BSS starts
* (if not in a DFS channel) or CAC ends.
*/
pHddApCtx->dfs_cac_block_tx = VOS_TRUE;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"The value of dfs_cac_block_tx[%d] for ApCtx[%pK]",
pHddApCtx->dfs_cac_block_tx, pHddApCtx);
if ((NV_CHANNEL_DFS ==
vos_nv_getChannelEnabledState(pHddApCtx->operatingChannel)) &&
(pHddCtx->cfg_ini->IsSapDfsChSifsBurstEnabled == 0))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Setting SIFS Burst disable for DFS channel %d",
__func__, pHddApCtx->operatingChannel);
if (process_wma_set_command((int)pHostapdAdapter->sessionId,
(int)WMI_PDEV_PARAM_BURST_ENABLE,
0, PDEV_CMD))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to Set SIFS Burst for DFS channel %d",
__func__, pHddApCtx->operatingChannel);
}
}
//Fill the params for sending IWEVCUSTOM Event with SOFTAP.enabled
startBssEvent = "SOFTAP.enabled";
memset(&we_custom_start_event, '\0', sizeof(we_custom_start_event));
memcpy(&we_custom_start_event, startBssEvent, strlen(startBssEvent));
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(startBssEvent);
we_event = IWEVCUSTOM;
we_custom_event_generic = we_custom_start_event;
hdd_dump_concurrency_info(pHddCtx);
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
if(pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP)
{
mutex_lock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock);
if(pHddCtx->ch_switch_ctx.is_ch_sw_through_sta_csa &&
(pHddApCtx->operatingChannel == pHddCtx->ch_switch_ctx.csa_to_channel)){
hddLog(LOG1, FL("Successfully Channel Switch is done to CH = %d"),
pHddApCtx->operatingChannel);
pHddCtx->ch_switch_ctx.is_ch_sw_through_sta_csa = VOS_FALSE;
}
mutex_unlock(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock);
}
#endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
break; //Event will be sent after Switch-Case stmt
case eSAP_STOP_BSS_EVENT:
hddLog(LOG1, FL("BSS stop status = %s"),pSapEvent->sapevt.sapStopBssCompleteEvent.status ?
"eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS");
hdd_set_sap_auth_offload(pHostapdAdapter, FALSE);
hdd_hostapd_channel_allow_suspend(pHostapdAdapter,
pHddApCtx->operatingChannel);
//Free up Channel List incase if it is set
sapCleanupChannelList(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter));
pHddApCtx->operatingChannel = 0; //Invalidate the channel info.
#ifdef IPA_OFFLOAD
if (hdd_ipa_is_enabled(pHddCtx))
{
status = hdd_ipa_wlan_evt(pHostapdAdapter, pHddApCtx->uBCStaId,
WLAN_AP_DISCONNECT, pHostapdAdapter->dev->dev_addr);
if (status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: WLAN_AP_DISCONNECT event failed!!"));
goto stopbss;
}
}
#endif
/* reset the dfs_cac_status and dfs_cac_block_tx flag only when
* the last BSS is stopped
*/
con_sap_adapter = hdd_get_con_sap_adapter(pHostapdAdapter, true);
if (!con_sap_adapter) {
pHddApCtx->dfs_cac_block_tx = TRUE;
pHddCtx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE;
}
if (pHddCtx->cfg_ini->conc_custom_rule2 &&
(WLAN_HDD_P2P_GO == pHostapdAdapter->device_mode)) {
hdd_adapter_t *sta_adapter = hdd_get_adapter(pHddCtx,
WLAN_HDD_INFRA_STATION);
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
FL("P2PGO is going down now"));
hdd_issue_stored_joinreq(sta_adapter, pHddCtx);
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("bss_stop_reason=%d"), pHddApCtx->bss_stop_reason);
if (pHddApCtx->bss_stop_reason != BSS_STOP_DUE_TO_MCC_SCC_SWITCH){
/* when MCC to SCC switching happens, key storage should not be
* cleared due to hostapd will not repopulate the original keys
*/
pHddApCtx->groupKey.keyLength = 0;
for (i = 0; i < CSR_MAX_NUM_KEY; i++)
pHddApCtx->wepKey[i].keyLength = 0;
}
/* clear the reason code in case BSS is stopped
* in another place
*/
pHddApCtx->bss_stop_reason = BSS_STOP_REASON_INVALID;
goto stopbss;
case eSAP_DFS_CAC_START:
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_DFS_CAC_START_IND,
&dfs_info,
sizeof(dfs_info));
pHddCtx->dev_dfs_cac_status = DFS_CAC_IN_PROGRESS;
if (VOS_STATUS_SUCCESS !=
hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_START,
dfs_info, &pHostapdAdapter->wdev)) {
hddLog(LOGE, FL("Unable to indicate CAC start NL event"));
} else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Sent CAC start to user space"));
}
pHddCtx->dfs_radar_found = VOS_FALSE;
break;
case eSAP_DFS_CAC_INTERRUPTED:
/*
* The CAC timer did not run completely and a radar was detected
* during the CAC time. This new state will keep the tx path
* blocked since we do not want any transmission on the DFS
* channel. CAC end will only be reported here since the user
* space applications are waiting on CAC end for their state
* management.
*/
if (VOS_STATUS_SUCCESS !=
hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_END,
dfs_info, &pHostapdAdapter->wdev)) {
hddLog(LOGE,
FL("Unable to indicate CAC end (interrupted) event"));
} else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Sent CAC end (interrupted) to user space"));
}
break;
case eSAP_DFS_CAC_END:
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_DFS_CAC_END_IND,
&dfs_info,
sizeof(dfs_info));
pHddApCtx->dfs_cac_block_tx = VOS_FALSE;
pHddCtx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE;
if (VOS_STATUS_SUCCESS !=
hdd_send_radar_event(pHddCtx, eSAP_DFS_CAC_END,
dfs_info, &pHostapdAdapter->wdev)) {
hddLog(LOGE, FL("Unable to indicate CAC end NL event"));
} else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Sent CAC end to user space"));
}
break;
case eSAP_DFS_RADAR_DETECT:
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_DFS_RADAR_DETECT_IND,
&dfs_info,
sizeof(dfs_info));
pHddCtx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE;
if (VOS_STATUS_SUCCESS !=
hdd_send_radar_event(pHddCtx, eSAP_DFS_RADAR_DETECT,
dfs_info, &pHostapdAdapter->wdev)) {
hddLog(LOGE, FL("Unable to indicate Radar detect NL event"));
} else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Sent radar detected to user space"));
}
break;
case eSAP_DFS_NO_AVAILABLE_CHANNEL:
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND,
&dfs_info,
sizeof(dfs_info));
break;
case eSAP_STA_SET_KEY_EVENT:
/* TODO: forward the message to hostapd once implementation
is done for now just print */
hddLog(LOG1, FL("SET Key: configured status = %s"),pSapEvent->sapevt.sapStationSetKeyCompleteEvent.status ?
"eSAP_STATUS_FAILURE" : "eSAP_STATUS_SUCCESS");
return VOS_STATUS_SUCCESS;
case eSAP_STA_DEL_KEY_EVENT:
/* TODO: forward the message to hostapd once implementation
is done for now just print */
hddLog(LOG1, FL("Event received %s"),"eSAP_STA_DEL_KEY_EVENT");
return VOS_STATUS_SUCCESS;
case eSAP_STA_MIC_FAILURE_EVENT:
{
memset(&msg, '\0', sizeof(msg));
msg.src_addr.sa_family = ARPHRD_ETHER;
memcpy(msg.src_addr.sa_data, &pSapEvent->sapevt.sapStationMICFailureEvent.staMac, sizeof(v_MACADDR_t));
hddLog(LOG1, "MIC MAC "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(msg.src_addr.sa_data));
if(pSapEvent->sapevt.sapStationMICFailureEvent.multicast == eSAP_TRUE)
msg.flags = IW_MICFAILURE_GROUP;
else
msg.flags = IW_MICFAILURE_PAIRWISE;
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = sizeof(msg);
we_event = IWEVMICHAELMICFAILURE;
we_custom_event_generic = (v_BYTE_t *)&msg;
}
/* inform mic failure to nl80211 */
cfg80211_michael_mic_failure(dev,
pSapEvent->sapevt.
sapStationMICFailureEvent.staMac.bytes,
((pSapEvent->sapevt.sapStationMICFailureEvent.multicast == eSAP_TRUE) ?
NL80211_KEYTYPE_GROUP :
NL80211_KEYTYPE_PAIRWISE),
pSapEvent->sapevt.sapStationMICFailureEvent.keyId,
pSapEvent->sapevt.sapStationMICFailureEvent.TSC,
GFP_KERNEL);
break;
case eSAP_STA_ASSOC_EVENT:
case eSAP_STA_REASSOC_EVENT:
event = &pSapEvent->sapevt.sapStationAssocReassocCompleteEvent;
wrqu.addr.sa_family = ARPHRD_ETHER;
memcpy(wrqu.addr.sa_data, &event->staMac,
sizeof(v_MACADDR_t));
hddLog(LOG1, " associated "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(wrqu.addr.sa_data));
we_event = IWEVREGISTERED;
#ifdef WLAN_FEATURE_MBSSID
WLANSAP_Get_WPS_State(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), &bWPSState);
#else
WLANSAP_Get_WPS_State((WLAN_HDD_GET_CTX(pHostapdAdapter))->pvosContext, &bWPSState);
#endif
if ( (eCSR_ENCRYPT_TYPE_NONE == pHddApCtx->ucEncryptType) ||
( eCSR_ENCRYPT_TYPE_WEP40_STATICKEY == pHddApCtx->ucEncryptType ) ||
( eCSR_ENCRYPT_TYPE_WEP104_STATICKEY == pHddApCtx->ucEncryptType ) )
{
bAuthRequired = FALSE;
}
if (bAuthRequired || bWPSState == eANI_BOOLEAN_TRUE )
{
vos_status = hdd_softap_RegisterSTA(pHostapdAdapter,
TRUE,
pHddApCtx->uPrivacy,
event->staId,
0,
0,
(v_MACADDR_t *)wrqu.addr.sa_data,
event->wmmEnabled);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGW, FL("Failed to register STA %d "MAC_ADDRESS_STR""),
vos_status, MAC_ADDR_ARRAY(wrqu.addr.sa_data));
}
else
{
vos_status = hdd_softap_RegisterSTA(pHostapdAdapter,
FALSE,
pHddApCtx->uPrivacy,
event->staId,
0,
0,
(v_MACADDR_t *)wrqu.addr.sa_data,
event->wmmEnabled);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGW, FL("Failed to register STA %d "MAC_ADDRESS_STR""),
vos_status, MAC_ADDR_ARRAY(wrqu.addr.sa_data));
}
if (VOS_IS_STATUS_SUCCESS(vos_status)) {
staId = event->staId;
hdd_fill_station_info(&pHostapdAdapter->aStaInfo[staId],
event);
pHostapdAdapter->aStaInfo[staId].ecsa_capable =
pSapEvent->
sapevt.sapStationAssocReassocCompleteEvent.ecsa_capable;
}
#ifdef IPA_OFFLOAD
if (hdd_ipa_is_enabled(pHddCtx))
{
status = hdd_ipa_wlan_evt(pHostapdAdapter,
event->staId,
WLAN_CLIENT_CONNECT_EX,
event->staMac.bytes);
if (status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, ("ERROR: WLAN_CLIENT_CONNECT_EX event failed!!"));
goto stopbss;
}
}
#endif
#ifdef QCA_PKT_PROTO_TRACE
/* Peer associated, update into trace buffer */
if (pHddCtx->cfg_ini->gEnableDebugLog)
{
vos_pkt_trace_buf_update("HA:ASSOC");
}
#endif /* QCA_PKT_PROTO_TRACE */
DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
pHostapdAdapter->sessionId,
ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_ASSOC));
#ifdef FEATURE_BUS_BANDWIDTH
/* start timer in sap/p2p_go */
if (pHddApCtx->bApActive == VOS_FALSE)
{
SPIN_LOCK_BH(&pHddCtx->bus_bw_lock);
pHostapdAdapter->prev_tx_packets = pHostapdAdapter->stats.tx_packets;
pHostapdAdapter->prev_rx_packets = pHostapdAdapter->stats.rx_packets;
tlshim_get_intra_bss_fwd_pkts_count(
pHostapdAdapter->sessionId,
&pHostapdAdapter->prev_fwd_tx_packets,
&pHostapdAdapter->prev_fwd_rx_packets);
pHostapdAdapter->prev_tx_bytes =
pHostapdAdapter->stats.tx_bytes;
SPIN_UNLOCK_BH(&pHddCtx->bus_bw_lock);
hdd_start_bus_bw_compute_timer(pHostapdAdapter);
}
#endif
pHddApCtx->bApActive = VOS_TRUE;
// Stop AP inactivity timer
if (pHddApCtx->hdd_ap_inactivity_timer.state == VOS_TIMER_STATE_RUNNING)
{
vos_status = vos_timer_stop(&pHddApCtx->hdd_ap_inactivity_timer);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to start AP inactivity timer"));
}
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_FALSE);
#endif
vos_wake_lock_timeout_acquire(&pHddCtx->sap_wake_lock,
HDD_SAP_WAKE_LOCK_DURATION,
WIFI_POWER_EVENT_WAKELOCK_SAP);
{
v_U16_t iesLen = event->iesLen;
if (iesLen <= MAX_ASSOC_IND_IE_LEN )
{
struct station_info *stainfo;
stainfo = vos_mem_malloc(sizeof(*stainfo));
if (stainfo == NULL) {
hddLog(LOGE, FL("alloc station_info failed"));
return VOS_STATUS_E_NOMEM;
}
memset(stainfo, 0, sizeof(*stainfo));
stainfo->assoc_req_ies =
(const u8 *)&event->ies[0];
stainfo->assoc_req_ies_len = iesLen;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
/*
* After Kernel 4.0, it's no longer need to set
* STATION_INFO_ASSOC_REQ_IES flag, as it
* changed to use assoc_req_ies_len length to
* check the existance of request IE.
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,31)) || defined(WITH_BACKPORTS)
stainfo->filled |= STATION_INFO_ASSOC_REQ_IES;
#endif
#endif
cfg80211_new_sta(dev,
(const u8 *)&event->staMac.bytes[0],
stainfo, GFP_KERNEL);
vos_mem_free(stainfo);
}
else
{
hddLog(LOGE, FL(" Assoc Ie length is too long"));
}
}
pScanInfo = &pHostapdAdapter->scan_info;
// Lets do abort scan to ensure smooth authentication for client
if ((pScanInfo != NULL) && pScanInfo->mScanPending)
{
hdd_abort_mac_scan(pHddCtx, pHostapdAdapter->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO)
{
/* send peer status indication to oem app */
hdd_SendPeerStatusIndToOemApp(
&event->staMac,
ePeerConnected,
event->timingMeasCap,
pHostapdAdapter->sessionId,
&event->chan_info,
pHostapdAdapter->device_mode);
}
hdd_wlan_green_ap_add_sta(pHddCtx);
if (pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP &&
!bAuthRequired && bWPSState == eANI_BOOLEAN_FALSE) {
uint32_t sub20_channelwidth;
if (hdd_hostapd_sub20_channelwidth_can_switch(
pHostapdAdapter, &sub20_channelwidth))
WLANSAP_set_sub20_channelwidth_with_csa(
WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter),
sub20_channelwidth);
}
break;
case eSAP_STA_DISASSOC_EVENT:
memcpy(wrqu.addr.sa_data, &pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac,
sizeof(v_MACADDR_t));
hddLog(LOG1, " disassociated "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(wrqu.addr.sa_data));
vos_status = vos_event_set(&pHostapdState->sta_disassoc_event);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(VOS_TRACE_LEVEL_ERROR,
"ERROR: Station deauth event reporting failed!!");
if (pSapEvent->sapevt.sapStationDisassocCompleteEvent.reason == eSAP_USR_INITATED_DISASSOC)
hddLog(LOG1," User initiated disassociation");
else
hddLog(LOG1," MAC initiated disassociation");
we_event = IWEVEXPIRED;
vos_status = hdd_softap_GetStaId(pHostapdAdapter, &pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac, &staId);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, FL("ERROR: HDD Failed to find sta id!!"));
return VOS_STATUS_E_FAILURE;
}
#ifdef IPA_OFFLOAD
if (!pHddCtx->isLogpInProgress && hdd_ipa_is_enabled(pHddCtx))
{
status = hdd_ipa_wlan_evt(pHostapdAdapter, staId, WLAN_CLIENT_DISCONNECT,
pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac.bytes);
if (status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("ERROR: WLAN_CLIENT_DISCONNECT event failed!!"));
goto stopbss;
}
}
#endif
#ifdef QCA_PKT_PROTO_TRACE
/* Peer dis-associated, update into trace buffer */
if (pHddCtx->cfg_ini->gEnableDebugLog)
{
vos_pkt_trace_buf_update("HA:DISASC");
}
#endif /* QCA_PKT_PROTO_TRACE */
DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
pHostapdAdapter->sessionId,
ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_DISASSOC));
hdd_softap_DeregisterSTA(pHostapdAdapter, staId);
pHddApCtx->bApActive = VOS_FALSE;
SPIN_LOCK_BH( &pHostapdAdapter->staInfo_lock );
for (i = 0; i < WLAN_MAX_STA_COUNT; i++)
{
if (pHostapdAdapter->aStaInfo[i].isUsed && i != (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uBCStaId)
{
pHddApCtx->bApActive = VOS_TRUE;
break;
}
}
SPIN_UNLOCK_BH( &pHostapdAdapter->staInfo_lock );
// Start AP inactivity timer if no stations associated with it
if ((0 != (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff))
{
if (pHddApCtx->bApActive == FALSE)
{
if (pHddApCtx->hdd_ap_inactivity_timer.state == VOS_TIMER_STATE_STOPPED)
{
vos_status = vos_timer_start(&pHddApCtx->hdd_ap_inactivity_timer, (WLAN_HDD_GET_CTX(pHostapdAdapter))->cfg_ini->nAPAutoShutOff * 1000);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to init AP inactivity timer"));
}
else
VOS_ASSERT(vos_timer_getCurrentState(&pHddApCtx->hdd_ap_inactivity_timer) == VOS_TIMER_STATE_STOPPED);
}
}
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
wlan_hdd_auto_shutdown_enable(pHddCtx, VOS_TRUE);
#endif
if (pSapEvent->sapevt.sapStationDisassocCompleteEvent.statusCode ==
eSIR_SME_SAP_AUTH_OFFLOAD_PEER_UPDATE_STATUS) {
/** eSIR_SME_SAP_AUTH_OFFLOAD_PEER_UPDATE_STATUS indicates:
* The existing sta connection needs to be updated instead
* of clean up the sta. This condition could only happens
* when Host SAP sleep with WOW and SAP Auth offload enabled.
*/
hddLog(LOG1,"SAP peer update sta:Id=%d, Mac="MAC_ADDRESS_STR,
pSapEvent->sapevt.sapStationDisassocCompleteEvent.staId,
MAC_ADDR_ARRAY(pSapEvent->sapevt.
sapStationDisassocCompleteEvent.staMac.bytes));
} else {
hddLog(LOG1,"SAP del sta: staId=%d, staMac="MAC_ADDRESS_STR,
pSapEvent->sapevt.sapStationDisassocCompleteEvent.staId,
MAC_ADDR_ARRAY(pSapEvent->sapevt.
sapStationDisassocCompleteEvent.staMac.bytes));
cfg80211_del_sta(dev,
(const u8 *)&pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac.bytes[0],
GFP_KERNEL);
}
//Update the beacon Interval if it is P2P GO
vos_status = hdd_change_mcc_go_beacon_interval(pHostapdAdapter);
if (VOS_STATUS_SUCCESS != vos_status)
{
hddLog(LOGE, "%s: failed to update Beacon interval %d",
__func__, vos_status);
}
if (pHostapdAdapter->device_mode == WLAN_HDD_P2P_GO)
{
/* send peer status indication to oem app */
hdd_SendPeerStatusIndToOemApp(
&pSapEvent->sapevt.sapStationDisassocCompleteEvent.staMac,
ePeerDisconnected, 0,
pHostapdAdapter->sessionId, NULL,
pHostapdAdapter->device_mode);
}
#ifdef FEATURE_BUS_BANDWIDTH
/*stop timer in sap/p2p_go */
if (pHddApCtx->bApActive == FALSE)
{
SPIN_LOCK_BH(&pHddCtx->bus_bw_lock);
pHostapdAdapter->prev_tx_packets = 0;
pHostapdAdapter->prev_rx_packets = 0;
pHostapdAdapter->prev_fwd_tx_packets = 0;
pHostapdAdapter->prev_fwd_rx_packets = 0;
pHostapdAdapter->prev_tx_bytes = 0;
SPIN_UNLOCK_BH(&pHddCtx->bus_bw_lock);
hdd_stop_bus_bw_compute_timer(pHostapdAdapter);
}
#endif
hdd_wlan_green_ap_del_sta(pHddCtx);
break;
case eSAP_WPS_PBC_PROBE_REQ_EVENT:
{
static const char * message ="MLMEWPSPBCPROBEREQ.indication";
union iwreq_data wreq;
down(&pHddApCtx->semWpsPBCOverlapInd);
pHddApCtx->WPSPBCProbeReq.probeReqIELen = pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.probeReqIELen;
vos_mem_copy(pHddApCtx->WPSPBCProbeReq.probeReqIE, pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.probeReqIE,
pHddApCtx->WPSPBCProbeReq.probeReqIELen);
vos_mem_copy(pHddApCtx->WPSPBCProbeReq.peerMacAddr, pSapEvent->sapevt.sapPBCProbeReqEvent.WPSPBCProbeReq.peerMacAddr, sizeof(v_MACADDR_t));
hddLog(LOG1, "WPS PBC probe req "MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pHddApCtx->WPSPBCProbeReq.peerMacAddr));
memset(&wreq, 0, sizeof(wreq));
wreq.data.length = strlen(message); // This is length of message
wireless_send_event(dev, IWEVCUSTOM, &wreq, (char *)message);
return VOS_STATUS_SUCCESS;
}
case eSAP_ASSOC_STA_CALLBACK_EVENT:
pAssocStasArray = pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas;
if (pSapEvent->sapevt.sapAssocStaListEvent.noOfAssocSta != 0)
{ // List of associated stations
for (i = 0; i < pSapEvent->sapevt.sapAssocStaListEvent.noOfAssocSta; i++)
{
hddLog(LOG1,"Associated Sta Num %d:assocId=%d, staId=%d, staMac="MAC_ADDRESS_STR,
i+1,
pAssocStasArray->assocId,
pAssocStasArray->staId,
MAC_ADDR_ARRAY(pAssocStasArray->staMac.bytes));
pAssocStasArray++;
}
}
vos_mem_free(pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas);// Release caller allocated memory here
pSapEvent->sapevt.sapAssocStaListEvent.pAssocStas = NULL;
return VOS_STATUS_SUCCESS;
case eSAP_REMAIN_CHAN_READY:
hdd_remainChanReadyHandler( pHostapdAdapter );
return VOS_STATUS_SUCCESS;
case eSAP_UNKNOWN_STA_JOIN:
snprintf(unknownSTAEvent, IW_CUSTOM_MAX, "JOIN_UNKNOWN_STA-%02x:%02x:%02x:%02x:%02x:%02x",
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[0],
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[1],
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[2],
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[3],
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[4],
pSapEvent->sapevt.sapUnknownSTAJoin.macaddr.bytes[5]);
we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */
wrqu.data.pointer = unknownSTAEvent;
wrqu.data.length = strlen(unknownSTAEvent);
we_custom_event_generic = (v_BYTE_t *)unknownSTAEvent;
hddLog(LOGE,"%s", unknownSTAEvent);
break;
case eSAP_MAX_ASSOC_EXCEEDED:
snprintf(maxAssocExceededEvent, IW_CUSTOM_MAX, "Peer %02x:%02x:%02x:%02x:%02x:%02x denied"
" assoc due to Maximum Mobile Hotspot connections reached. Please disconnect"
" one or more devices to enable the new device connection",
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[0],
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[1],
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[2],
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[3],
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[4],
pSapEvent->sapevt.sapMaxAssocExceeded.macaddr.bytes[5]);
we_event = IWEVCUSTOM; /* Discovered a new node (AP mode). */
wrqu.data.pointer = maxAssocExceededEvent;
wrqu.data.length = strlen(maxAssocExceededEvent);
we_custom_event_generic = (v_BYTE_t *)maxAssocExceededEvent;
hddLog(LOG1,"%s", maxAssocExceededEvent);
break;
case eSAP_STA_ASSOC_IND:
return VOS_STATUS_SUCCESS;
case eSAP_DISCONNECT_ALL_P2P_CLIENT:
hddLog(LOG1, FL(" Disconnecting all the P2P Clients...."));
hdd_clear_all_sta(pHostapdAdapter, usrDataForCallback);
return VOS_STATUS_SUCCESS;
case eSAP_MAC_TRIG_STOP_BSS_EVENT :
vos_status = hdd_stop_bss_link(pHostapdAdapter, usrDataForCallback);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGW, FL("hdd_stop_bss_link failed %d"), vos_status);
return VOS_STATUS_SUCCESS;
case eSAP_CHANNEL_CHANGE_EVENT:
hddLog(LOG1, FL("Received eSAP_CHANNEL_CHANGE_EVENT event"));
if (pHostapdState->bssState != BSS_STOP) {
/* Prevent suspend for new channel */
hdd_hostapd_channel_prevent_suspend(pHostapdAdapter,
pSapEvent->sapevt.sapChSelected.pri_ch);
/* Allow suspend for old channel */
hdd_hostapd_channel_allow_suspend(pHostapdAdapter,
pHddApCtx->operatingChannel);
}
/* SME/PE is already updated for new operation channel. So update
* HDD layer also here. This resolves issue in AP-AP mode where
* AP1 channel is changed due to RADAR then CAC is going on and
* START_BSS on new channel has not come to HDD. At this case if
* AP2 is start it needs current operation channel for MCC DFS
* restiction
*/
pHddApCtx->operatingChannel =
pSapEvent->sapevt.sapChSelected.pri_ch;
pHddApCtx->sapConfig.acs_cfg.pri_ch =
pSapEvent->sapevt.sapChSelected.pri_ch;
pHddApCtx->sapConfig.acs_cfg.ht_sec_ch =
pSapEvent->sapevt.sapChSelected.ht_sec_ch;
pHddApCtx->sapConfig.acs_cfg.vht_seg0_center_ch =
pSapEvent->sapevt.sapChSelected.vht_seg0_center_ch;
pHddApCtx->sapConfig.acs_cfg.vht_seg1_center_ch =
pSapEvent->sapevt.sapChSelected.vht_seg1_center_ch;
pHddApCtx->sapConfig.acs_cfg.ch_width =
pSapEvent->sapevt.sapChSelected.ch_width;
/* Indicate operating channel change to hostapd
* only for non driver override acs
*/
if (pHostapdAdapter->device_mode == WLAN_HDD_SOFTAP &&
pHddCtx->cfg_ini->force_sap_acs)
return VOS_STATUS_SUCCESS;
else
return hdd_chan_change_notify(pHostapdAdapter, dev,
pSapEvent->sapevt.sapChSelected.pri_ch);
case eSAP_ACS_SCAN_SUCCESS_EVENT:
return hdd_handle_acs_scan_event(pSapEvent, pHostapdAdapter);
case eSAP_DFS_NOL_GET:
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Received eSAP_DFS_NOL_GET event"));
/* get the dfs nol from cnss */
return hdd_wlan_get_dfs_nol(
pSapEvent->sapevt.sapDfsNolInfo.pDfsList,
pSapEvent->sapevt.sapDfsNolInfo.sDfsList);
case eSAP_DFS_NOL_SET:
hddLog(VOS_TRACE_LEVEL_INFO, FL("Received eSAP_DFS_NOL_SET event"));
/* set the dfs nol to cnss */
return hdd_wlan_set_dfs_nol(
pSapEvent->sapevt.sapDfsNolInfo.pDfsList,
pSapEvent->sapevt.sapDfsNolInfo.sDfsList);
case eSAP_ACS_CHANNEL_SELECTED:
hddLog(LOG1, FL("ACS Completed for wlan%d"),
pHostapdAdapter->dev->ifindex);
clear_bit(ACS_PENDING, &pHostapdAdapter->event_flags);