blob: 0034d5c7975008eb97b886ad2b0129c76cc2b78a [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_main.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.
02/24/10 Sudhir.S.Kohalli Added to support param for SoftAP module
06/03/10 js - Added support to hostapd driven deauth/disassoc/mic failure
==========================================================================*/
/*--------------------------------------------------------------------------
Include Files
------------------------------------------------------------------------*/
//#include <wlan_qct_driver.h>
#include <wlan_hdd_includes.h>
#include <vos_api.h>
#include <vos_nvitem.h>
#include <vos_sched.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#ifdef ANI_BUS_TYPE_PLATFORM
#include <linux/wcnss_wlan.h>
#endif //ANI_BUS_TYPE_PLATFORM
#ifdef ANI_BUS_TYPE_PCI
#include "wcnss_wlan.h"
#endif /* ANI_BUS_TYPE_PCI */
#include <wlan_hdd_tx_rx.h>
#include <palTimer.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_logging_sock_svc.h>
#include <wlan_hdd_wowl.h>
#include <wlan_hdd_misc.h>
#include <wlan_hdd_wext.h>
#ifdef WLAN_BTAMP_FEATURE
#include <bap_hdd_main.h>
#include <bapInternal.h>
#endif // WLAN_BTAMP_FEATURE
#include "wlan_hdd_trace.h"
#include "vos_types.h"
#include "vos_trace.h"
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include <linux/inetdevice.h>
#include <net/addrconf.h>
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_p2p.h"
#include <linux/rtnetlink.h>
int wlan_hdd_ftm_start(hdd_context_t *pAdapter);
#include "sapApi.h"
#include <linux/semaphore.h>
#include <linux/ctype.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
#include <soc/qcom/subsystem_restart.h>
#else
#include <mach/subsystem_restart.h>
#endif
#include <wlan_hdd_hostapd.h>
#include <wlan_hdd_softap_tx_rx.h>
#include "cfgApi.h"
#include "wlan_hdd_dev_pwr.h"
#ifdef WLAN_BTAMP_FEATURE
#include "bap_hdd_misc.h"
#endif
#include "wlan_qct_pal_trace.h"
#include "qwlan_version.h"
#include "wlan_qct_wda.h"
#ifdef FEATURE_WLAN_TDLS
#include "wlan_hdd_tdls.h"
#endif
#include "wlan_hdd_debugfs.h"
#include "sapInternal.h"
#include "wlan_hdd_request_manager.h"
#ifdef MODULE
#define WLAN_MODULE_NAME module_name(THIS_MODULE)
#else
#define WLAN_MODULE_NAME "wlan"
#endif
#ifdef TIMER_MANAGER
#define TIMER_MANAGER_STR " +TIMER_MANAGER"
#else
#define TIMER_MANAGER_STR ""
#endif
#ifdef MEMORY_DEBUG
#define MEMORY_DEBUG_STR " +MEMORY_DEBUG"
#else
#define MEMORY_DEBUG_STR ""
#endif
#define MAX_WAIT_FOR_ROC_COMPLETION 3
/* the Android framework expects this param even though we don't use it */
#define BUF_LEN 20
static char fwpath_buffer[BUF_LEN];
static struct kparam_string fwpath = {
.string = fwpath_buffer,
.maxlen = BUF_LEN,
};
static char *country_code;
static int enable_11d = -1;
static int enable_dfs_chan_scan = -1;
#ifndef MODULE
static int wlan_hdd_inited;
#endif
/*
* spinlock for synchronizing asynchronous request/response
* (full description of use in wlan_hdd_main.h)
*/
DEFINE_SPINLOCK(hdd_context_lock);
/*
* The rate at which the driver sends RESTART event to supplicant
* once the function 'vos_wlanRestart()' is called
*
*/
#define WLAN_HDD_RESTART_RETRY_DELAY_MS 5000 /* 5 second */
#define WLAN_HDD_RESTART_RETRY_MAX_CNT 5 /* 5 retries */
/*
* Size of Driver command strings from upper layer
*/
#define SIZE_OF_SETROAMMODE 11 /* size of SETROAMMODE */
#define SIZE_OF_GETROAMMODE 11 /* size of GETROAMMODE */
#ifdef WLAN_FEATURE_RMC
/*
* Ibss prop IE from command will be of size:
* size = sizeof(oui) + sizeof(oui_data) + 1(Element ID) + 1(EID Length)
* OUI_DATA should be at least 3 bytes long
*/
#define WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH (3)
#endif
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
#define TID_MIN_VALUE 0
#define TID_MAX_VALUE 15
static VOS_STATUS hdd_get_tsm_stats(hdd_adapter_t *pAdapter, const tANI_U8 tid,
tAniTrafStrmMetrics* pTsmMetrics);
static VOS_STATUS hdd_parse_ese_beacon_req(tANI_U8 *pValue,
tCsrEseBeaconReq *pEseBcnReq);
#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
/*
* Maximum buffer size used for returning the data back to user space
*/
#define WLAN_MAX_BUF_SIZE 1024
#define WLAN_PRIV_DATA_MAX_LEN 8192
/*
* When ever we need to print IBSSPEERINFOALL for morethan 16 STA
* we will split the printing.
*/
#define NUM_OF_STA_DATA_TO_PRINT 16
#ifdef WLAN_FEATURE_RMC
#define WLAN_NLINK_CESIUM 30
#endif
//wait time for beacon miss rate.
#define BCN_MISS_RATE_TIME 500
/*
* Android DRIVER command structures
*/
struct android_wifi_reassoc_params {
unsigned char bssid[18];
int channel;
};
static vos_wake_lock_t wlan_wake_lock;
/* set when SSR is needed after unload */
static e_hdd_ssr_required isSsrRequired = HDD_SSR_NOT_REQUIRED;
//internal function declaration
static VOS_STATUS wlan_hdd_framework_restart(hdd_context_t *pHddCtx);
static void wlan_hdd_restart_init(hdd_context_t *pHddCtx);
static void wlan_hdd_restart_deinit(hdd_context_t *pHddCtx);
#ifdef WLAN_FEATURE_RMC
static void hdd_tx_fail_ind_callback(v_U8_t *MacAddr, v_U8_t seqNo);
static int hdd_open_cesium_nl_sock(void);
static void hdd_close_cesium_nl_sock(void);
static struct sock *cesium_nl_srv_sock;
static v_U16_t cesium_pid;
static int hdd_ParseIBSSTXFailEventParams(tANI_U8 *pValue,
tANI_U8 *tx_fail_count,
tANI_U16 *pid);
static int hdd_ParseUserParams(tANI_U8 *pValue, tANI_U8 **ppArg);
#endif /* WLAN_FEATURE_RMC */
void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback);
void hdd_set_wlan_suspend_mode(bool suspend);
void hdd_set_vowifi_mode(hdd_context_t *hdd_ctx, bool enable);
v_U16_t hdd_select_queue(struct net_device *dev,
struct sk_buff *skb
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0))
, void *accel_priv
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
, select_queue_fallback_t fallback
#endif
);
#ifdef WLAN_FEATURE_PACKET_FILTERING
static void hdd_set_multicast_list(struct net_device *dev);
#endif
void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter);
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
void hdd_getBand_helper(hdd_context_t *pHddCtx, int *pBand);
static VOS_STATUS hdd_parse_channellist(tANI_U8 *pValue, tANI_U8 *pChannelList, tANI_U8 *pNumChannels);
static VOS_STATUS hdd_parse_send_action_frame_data(tANI_U8 *pValue, tANI_U8 *pTargetApBssid,
tANI_U8 *pChannel, tANI_U8 *pDwellTime,
tANI_U8 **pBuf, tANI_U8 *pBufLen);
static int hdd_parse_reassoc_command_v1_data(const tANI_U8 *pValue,
tANI_U8 *pTargetApBssid, tANI_U8 *pChannel);
#endif
/* Store WLAN driver info in a global variable such that crash debugger
can extract it from driver debug symbol and crashdump for post processing */
tANI_U8 g_wlan_driver[ ] = "pronto_driver";
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
VOS_STATUS hdd_parse_get_cckm_ie(tANI_U8 *pValue, tANI_U8 **pCckmIe, tANI_U8 *pCckmIeLen);
#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
static VOS_STATUS wlan_hdd_init_channels(hdd_context_t *pHddCtx);
const char * hdd_device_modetoString(v_U8_t device_mode)
{
switch(device_mode)
{
CASE_RETURN_STRING( WLAN_HDD_INFRA_STATION );
CASE_RETURN_STRING( WLAN_HDD_SOFTAP );
CASE_RETURN_STRING( WLAN_HDD_P2P_CLIENT );
CASE_RETURN_STRING( WLAN_HDD_P2P_GO );
CASE_RETURN_STRING( WLAN_HDD_MONITOR);
CASE_RETURN_STRING( WLAN_HDD_FTM );
CASE_RETURN_STRING( WLAN_HDD_IBSS );
CASE_RETURN_STRING( WLAN_HDD_P2P_DEVICE );
default:
return "device_mode Unknown";
}
}
static int __hdd_netdev_notifier_call(struct notifier_block * nb,
unsigned long state,
void *ndev)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
struct netdev_notifier_info *info = ndev;
struct net_device *dev = info->dev;
#else
struct net_device *dev = ndev;
#endif
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
#ifdef WLAN_BTAMP_FEATURE
VOS_STATUS status;
#endif
long result;
//Make sure that this callback corresponds to our device.
if ((strncmp(dev->name, "wlan", 4)) &&
(strncmp(dev->name, "p2p", 3)))
return NOTIFY_DONE;
if (!dev->ieee80211_ptr)
return NOTIFY_DONE;
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD Adapter Null Pointer", __func__);
VOS_ASSERT(0);
return NOTIFY_DONE;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD Context Null Pointer", __func__);
VOS_ASSERT(0);
return NOTIFY_DONE;
}
if (pHddCtx->isLogpInProgress)
return NOTIFY_DONE;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: %s New Net Device State = %lu",
__func__, dev->name, state);
switch (state) {
case NETDEV_REGISTER:
break;
case NETDEV_UNREGISTER:
break;
case NETDEV_UP:
break;
case NETDEV_DOWN:
break;
case NETDEV_CHANGE:
if(TRUE == pAdapter->isLinkUpSvcNeeded)
complete(&pAdapter->linkup_event_var);
break;
case NETDEV_GOING_DOWN:
result = wlan_hdd_scan_abort(pAdapter);
if (result < 0)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Timeout occurred while waiting for abortscan %ld",
__func__, result);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Scan Abort Successful" , __func__);
}
#ifdef WLAN_BTAMP_FEATURE
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"%s: disabling AMP", __func__);
status = WLANBAP_StopAmp();
if(VOS_STATUS_SUCCESS != status )
{
pHddCtx->isAmpAllowed = VOS_TRUE;
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to stop AMP", __func__);
}
else
{
//a state m/c implementation in PAL is TBD to avoid this delay
msleep(500);
if ( pHddCtx->isAmpAllowed )
{
WLANBAP_DeregisterFromHCI();
pHddCtx->isAmpAllowed = VOS_FALSE;
}
}
#endif //WLAN_BTAMP_FEATURE
break;
default:
break;
}
return NOTIFY_DONE;
}
static int hdd_netdev_notifier_call(struct notifier_block * nb,
unsigned long state,
void *ndev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_netdev_notifier_call( nb, state, ndev);
vos_ssr_unprotect(__func__);
return ret;
}
struct notifier_block hdd_netdev_notifier = {
.notifier_call = hdd_netdev_notifier_call,
};
/*---------------------------------------------------------------------------
* Function definitions
*-------------------------------------------------------------------------*/
void hdd_unregister_mcast_bcast_filter(hdd_context_t *pHddCtx);
void hdd_register_mcast_bcast_filter(hdd_context_t *pHddCtx);
//variable to hold the insmod parameters
static int con_mode;
#ifndef MODULE
/* current con_mode - used only for statically linked driver
* con_mode is changed by userspace to indicate a mode change which will
* result in calling the module exit and init functions. The module
* exit function will clean up based on the value of con_mode prior to it
* being changed by userspace. So curr_con_mode records the current con_mode
* for exit when con_mode becomes the next mode for init
*/
static int curr_con_mode;
#endif
#ifdef WLAN_FEATURE_OFFLOAD_PACKETS
/**
* hdd_init_offloaded_packets_ctx() - Initialize offload packets context
* @hdd_ctx: hdd global context
*
* Return: none
*/
static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx)
{
uint8_t i;
mutex_init(&hdd_ctx->op_ctx.op_lock);
for (i = 0; i < MAXNUM_PERIODIC_TX_PTRNS; i++)
{
hdd_ctx->op_ctx.op_table[i].request_id = 0;
hdd_ctx->op_ctx.op_table[i].pattern_id = i;
}
}
#else
static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx)
{
}
#endif
/**---------------------------------------------------------------------------
\brief hdd_vos_trace_enable() - Configure initial VOS Trace enable
Called immediately after the cfg.ini is read in order to configure
the desired trace levels.
\param - moduleId - module whose trace level is being configured
\param - bitmask - bitmask of log levels to be enabled
\return - void
--------------------------------------------------------------------------*/
static void hdd_vos_trace_enable(VOS_MODULE_ID moduleId, v_U32_t bitmask)
{
wpt_tracelevel level;
/* if the bitmask is the default value, then a bitmask was not
specified in cfg.ini, so leave the logging level alone (it
will remain at the "compiled in" default value) */
if (CFG_VOS_TRACE_ENABLE_DEFAULT == bitmask)
{
return;
}
/* a mask was specified. start by disabling all logging */
vos_trace_setValue(moduleId, VOS_TRACE_LEVEL_NONE, 0);
/* now cycle through the bitmask until all "set" bits are serviced */
level = VOS_TRACE_LEVEL_FATAL;
while (0 != bitmask)
{
if (bitmask & 1)
{
vos_trace_setValue(moduleId, level, 1);
}
level++;
bitmask >>= 1;
}
}
/**---------------------------------------------------------------------------
\brief hdd_wdi_trace_enable() - Configure initial WDI Trace enable
Called immediately after the cfg.ini is read in order to configure
the desired trace levels in the WDI.
\param - moduleId - module whose trace level is being configured
\param - bitmask - bitmask of log levels to be enabled
\return - void
--------------------------------------------------------------------------*/
static void hdd_wdi_trace_enable(wpt_moduleid moduleId, v_U32_t bitmask)
{
wpt_tracelevel level;
/* if the bitmask is the default value, then a bitmask was not
specified in cfg.ini, so leave the logging level alone (it
will remain at the "compiled in" default value) */
if (CFG_WDI_TRACE_ENABLE_DEFAULT == bitmask)
{
return;
}
/* a mask was specified. start by disabling all logging */
wpalTraceSetLevel(moduleId, eWLAN_PAL_TRACE_LEVEL_NONE, 0);
/* now cycle through the bitmask until all "set" bits are serviced */
level = eWLAN_PAL_TRACE_LEVEL_FATAL;
while (0 != bitmask)
{
if (bitmask & 1)
{
wpalTraceSetLevel(moduleId, level, 1);
}
level++;
bitmask >>= 1;
}
}
/*
* FUNCTION: wlan_hdd_validate_context
* This function is used to check the HDD context
*/
int wlan_hdd_validate_context(hdd_context_t *pHddCtx)
{
if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: HDD context is Null", __func__);
return -ENODEV;
}
if (pHddCtx->isLogpInProgress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: LOGP %s. Ignore!!", __func__,
vos_is_wlan_in_badState(VOS_MODULE_ID_HDD, NULL)
?"failed":"in Progress");
return -EAGAIN;
}
if (WLAN_HDD_IS_LOAD_UNLOAD_IN_PROGRESS(pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Unloading/Loading in Progress. Ignore!!!", __func__);
return -EAGAIN;
}
return 0;
}
#ifdef CONFIG_ENABLE_LINUX_REG
void hdd_checkandupdate_phymode( hdd_context_t *pHddCtx)
{
hdd_adapter_t *pAdapter = NULL;
hdd_station_ctx_t *pHddStaCtx = NULL;
eCsrPhyMode phyMode;
hdd_config_t *cfg_param = NULL;
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"HDD Context is null !!");
return ;
}
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"pAdapter is null !!");
return ;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL == pHddStaCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"pHddStaCtx is null !!");
return ;
}
cfg_param = pHddCtx->cfg_ini;
if (NULL == cfg_param)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"cfg_params not available !!");
return ;
}
phyMode = sme_GetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter));
if (!pHddCtx->isVHT80Allowed)
{
if ((eCSR_DOT11_MODE_AUTO == phyMode) ||
(eCSR_DOT11_MODE_11ac == phyMode) ||
(eCSR_DOT11_MODE_11ac_ONLY == phyMode))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Setting phymode to 11n!!");
sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter), eCSR_DOT11_MODE_11n);
}
}
else
{
/*New country Supports 11ac as well resetting value back from .ini*/
sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter),
hdd_cfg_xlate_to_csr_phy_mode(cfg_param->dot11Mode));
return ;
}
if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
((eCSR_CFG_DOT11_MODE_11AC_ONLY == pHddStaCtx->conn_info.dot11Mode) ||
(eCSR_CFG_DOT11_MODE_11AC == pHddStaCtx->conn_info.dot11Mode)))
{
VOS_STATUS vosStatus;
// need to issue a disconnect to CSR.
INIT_COMPLETION(pAdapter->disconnect_comp_var);
vosStatus = sme_RoamDisconnect(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED );
if (VOS_STATUS_SUCCESS == vosStatus)
{
long ret;
ret = wait_for_completion_interruptible_timeout(&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (0 >= ret)
hddLog(LOGE, FL("failure waiting for disconnect_comp_var %ld"),
ret);
}
}
}
#else
void hdd_checkandupdate_phymode( hdd_adapter_t *pAdapter, char *country_code)
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_config_t *cfg_param;
eCsrPhyMode phyMode;
long ret;
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"HDD Context is null !!");
return ;
}
cfg_param = pHddCtx->cfg_ini;
if (NULL == cfg_param)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"cfg_params not available !!");
return ;
}
phyMode = sme_GetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter));
if (NULL != strstr(cfg_param->listOfNon11acCountryCode, country_code))
{
if ((eCSR_DOT11_MODE_AUTO == phyMode) ||
(eCSR_DOT11_MODE_11ac == phyMode) ||
(eCSR_DOT11_MODE_11ac_ONLY == phyMode))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Setting phymode to 11n!!");
sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter), eCSR_DOT11_MODE_11n);
}
}
else
{
/*New country Supports 11ac as well resetting value back from .ini*/
sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter),
hdd_cfg_xlate_to_csr_phy_mode(cfg_param->dot11Mode));
return ;
}
if ((eConnectionState_Associated == pHddStaCtx->conn_info.connState) &&
((eCSR_CFG_DOT11_MODE_11AC_ONLY == pHddStaCtx->conn_info.dot11Mode) ||
(eCSR_CFG_DOT11_MODE_11AC == pHddStaCtx->conn_info.dot11Mode)))
{
VOS_STATUS vosStatus;
// need to issue a disconnect to CSR.
INIT_COMPLETION(pAdapter->disconnect_comp_var);
vosStatus = sme_RoamDisconnect(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED );
if (VOS_STATUS_SUCCESS == vosStatus)
{
ret = wait_for_completion_interruptible_timeout(&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (ret <= 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"wait on disconnect_comp_var is failed %ld", ret);
}
}
}
}
#endif //CONFIG_ENABLE_LINUX_REG
void hdd_checkandupdate_dfssetting( hdd_adapter_t *pAdapter, char *country_code)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_config_t *cfg_param;
if (NULL == pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"HDD Context is null !!");
return ;
}
cfg_param = pHddCtx->cfg_ini;
if (NULL == cfg_param)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"cfg_params not available !!");
return ;
}
if (NULL != strstr(cfg_param->listOfNonDfsCountryCode, country_code) ||
pHddCtx->disable_dfs_flag == TRUE)
{
/*New country doesn't support DFS */
sme_UpdateDfsSetting(WLAN_HDD_GET_HAL_CTX(pAdapter), 0);
}
else
{
/* New country Supports DFS as well resetting value back from .ini */
sme_UpdateDfsSetting(WLAN_HDD_GET_HAL_CTX(pAdapter),
cfg_param->enableDFSChnlScan);
}
}
#ifdef WLAN_FEATURE_RMC
static int hdd_parse_setrmcenable_command(tANI_U8 *pValue, tANI_U8 *pRmcEnable)
{
tANI_U8 *inPtr = pValue;
int tempInt;
int v = 0;
char buf[32];
*pRmcEnable = 0;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return 0;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return 0;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return 0;
}
/* getting the first argument which enables or disables RMC
* for input IP v4 address*/
sscanf(inPtr, "%31s ", buf);
v = kstrtos32(buf, 10, &tempInt);
if ( v < 0)
{
return -EINVAL;
}
*pRmcEnable = tempInt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"ucRmcEnable: %d", *pRmcEnable);
return 0;
}
/* Function header left blank Intentionally */
static int hdd_parse_setrmcactionperiod_command(tANI_U8 *pValue,
tANI_U32 *pActionPeriod)
{
tANI_U8 *inPtr = pValue;
int tempInt;
int v = 0;
char buf[32];
*pActionPeriod = 0;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return 0;
}
/* getting the first argument which enables or disables RMC
* for input IP v4 address*/
sscanf(inPtr, "%31s ", buf);
v = kstrtos32(buf, 10, &tempInt);
if ( v < 0)
{
return -EINVAL;
}
/* Range checking for passed paramter */
if (tempInt < WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMIN ||
tempInt > WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY_STAMAX)
{
return -EINVAL;
}
*pActionPeriod = tempInt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"uActionPeriod: %d", *pActionPeriod);
return 0;
}
/**
* hdd_set_vowifi_mode() - Process VOWIFI command.
* @hdd_ctx: context handler
* @enable: Value to be sent as a part of the VOWIFI command
*
* Invoke the SME api if station is connected in 2.4 GHz band.
* Also start split scan if VOWIFIMODE and dynamic split scan
* both are enabled.
* Return: void
*/
void hdd_set_vowifi_mode(hdd_context_t *hdd_ctx, bool enable)
{
tANI_U8 sta_chan;
if (!hdd_ctx->cfg_ini) {
hddLog(LOGE, "cfg_ini got NULL");
return;
}
sta_chan = hdd_get_operating_channel(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (CSR_IS_CHANNEL_24GHZ(sta_chan))
sme_set_vowifi_mode(hdd_ctx->hHal, enable);
else
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"VoWiFi command rejected as not connected in 2.4GHz");
if (enable && hdd_ctx->cfg_ini->dynSplitscan) {
hdd_ctx->is_vowifi_enabled = true;
hdd_ctx->issplitscan_enabled = TRUE;
sme_enable_disable_split_scan(hdd_ctx->hHal,
hdd_ctx->cfg_ini->nNumStaChanCombinedConc,
hdd_ctx->cfg_ini->nNumP2PChanCombinedConc);
} else {
hdd_ctx->is_vowifi_enabled = false;
}
}
/* Function header left blank Intentionally */
static int hdd_parse_setrmcrate_command(tANI_U8 *pValue,
tANI_U32 *pRate, tTxrateinfoflags *pTxFlags)
{
tANI_U8 *inPtr = pValue;
int tempInt;
int v = 0;
char buf[32];
*pRate = 0;
*pTxFlags = 0;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return 0;
}
/*
* getting the first argument which sets multicast rate.
*/
sscanf(inPtr, "%31s ", buf);
v = kstrtos32(buf, 10, &tempInt);
if ( v < 0)
{
return -EINVAL;
}
/*
* Validate the multicast rate.
*/
switch (tempInt)
{
default:
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"Unsupported rate: %d", tempInt);
return -EINVAL;
case 0:
case 6:
case 9:
case 12:
case 18:
case 24:
case 36:
case 48:
case 54:
*pTxFlags = eHAL_TX_RATE_LEGACY;
*pRate = tempInt * 10;
break;
case 65:
*pTxFlags = eHAL_TX_RATE_HT20;
*pRate = tempInt * 10;
break;
case 72:
*pTxFlags = eHAL_TX_RATE_HT20 | eHAL_TX_RATE_SGI;
*pRate = 722; /* fractional rate 72.2 Mbps */
break;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rate: %d", *pRate);
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_cfg80211_get_ibss_peer_info_cb() - Callback function for IBSS
Peer Info request
This is an asynchronous callback function from SME when the peer info
is received
\pUserData -> Adapter private data
\pPeerInfoRsp -> Peer info response
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static void
hdd_cfg80211_get_ibss_peer_info_cb(v_VOID_t *pUserData, v_VOID_t *pPeerInfoRsp)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *)pUserData;
tSirPeerInfoRspParams *pPeerInfo = (tSirPeerInfoRspParams *)pPeerInfoRsp;
hdd_station_ctx_t *pStaCtx;
v_U8_t i;
/*Sanity check*/
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
hddLog(LOGE,
FL("invalid adapter or adapter has invalid magic"));
return;
}
pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL != pStaCtx && NULL != pPeerInfo &&
eHAL_STATUS_SUCCESS == pPeerInfo->status)
{
pStaCtx->ibss_peer_info.status = pPeerInfo->status;
pStaCtx->ibss_peer_info.numIBSSPeers = pPeerInfo->numPeers;
/* Paranoia check */
if (pPeerInfo->numPeers < HDD_MAX_NUM_IBSS_STA)
{
for (i = 0; i < pPeerInfo->numPeers; i++)
{
memcpy(&pStaCtx->ibss_peer_info.ibssPeerList[i],
&pPeerInfo->peerInfoParams[i],
sizeof(hdd_ibss_peer_info_params_t));
}
hddLog(LOG1,
FL("Peer Info copied in HDD"));
}
else
{
hddLog(LOGE,
FL(" Number of peers %d returned is more than limit %d"),
pPeerInfo->numPeers, HDD_MAX_NUM_IBSS_STA);
}
}
else
{
hddLog(LOG1,
FL("peerInfo returned is NULL"));
}
complete(&pAdapter->ibss_peer_info_comp);
}
/**---------------------------------------------------------------------------
\brief hdd_cfg80211_get_ibss_peer_info_all() -
Request function to get IBSS peer info from lower layers
\pAdapter -> Adapter context
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static
VOS_STATUS hdd_cfg80211_get_ibss_peer_info_all(hdd_adapter_t *pAdapter)
{
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
long status;
VOS_STATUS retStatus = VOS_STATUS_E_FAILURE;
INIT_COMPLETION(pAdapter->ibss_peer_info_comp);
retStatus = sme_RequestIBSSPeerInfo(hHal, pAdapter,
hdd_cfg80211_get_ibss_peer_info_cb,
VOS_TRUE, 0xFF);
if (VOS_STATUS_SUCCESS == retStatus)
{
status = wait_for_completion_interruptible_timeout
(&pAdapter->ibss_peer_info_comp,
msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
/* status will be 0 if timed out */
if (status <= 0)
{
hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT %ld",
__func__, status);
retStatus = VOS_STATUS_E_FAILURE;
return retStatus;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Warning: sme_RequestIBSSPeerInfo Request failed", __func__);
}
return retStatus;
}
/**---------------------------------------------------------------------------
\brief hdd_cfg80211_get_ibss_peer_info() -
Request function to get IBSS peer info from lower layers
\pAdapter -> Adapter context
\staIdx -> Sta index for which the peer info is requested
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static VOS_STATUS
hdd_cfg80211_get_ibss_peer_info(hdd_adapter_t *pAdapter, v_U8_t staIdx)
{
long status;
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
VOS_STATUS retStatus = VOS_STATUS_E_FAILURE;
INIT_COMPLETION(pAdapter->ibss_peer_info_comp);
retStatus = sme_RequestIBSSPeerInfo(hHal, pAdapter,
hdd_cfg80211_get_ibss_peer_info_cb,
VOS_FALSE, staIdx);
if (VOS_STATUS_SUCCESS == retStatus)
{
status = wait_for_completion_interruptible_timeout
(&pAdapter->ibss_peer_info_comp,
msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
/* status = 0 on timeout */
if (status <= 0)
{
hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT %ld",
__func__, status);
retStatus = VOS_STATUS_E_FAILURE;
return retStatus;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Warning: sme_RequestIBSSPeerInfo Request failed", __func__);
}
return retStatus;
}
/* Function header left blank Intentionally */
VOS_STATUS
hdd_parse_get_ibss_peer_info(tANI_U8 *pValue, v_MACADDR_t *pPeerMacAddr)
{
tANI_U8 *inPtr = pValue;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
/*getting the first argument ie the peer mac address */
if (inPtr[2] != ':' || inPtr[5] != ':' || inPtr[8] != ':' ||
inPtr[11] != ':' || inPtr[14] != ':')
{
return VOS_STATUS_E_FAILURE;;
}
sscanf(inPtr, "%2x:%2x:%2x:%2x:%2x:%2x",
(unsigned int *)&pPeerMacAddr->bytes[0],
(unsigned int *)&pPeerMacAddr->bytes[1],
(unsigned int *)&pPeerMacAddr->bytes[2],
(unsigned int *)&pPeerMacAddr->bytes[3],
(unsigned int *)&pPeerMacAddr->bytes[4],
(unsigned int *)&pPeerMacAddr->bytes[5]);
/* The command buffer seems to be fine */
return VOS_STATUS_SUCCESS;
}
/* Function header left blank Intentionally */
static int hdd_parse_set_ibss_oui_data_command(tANI_U8 *command, tANI_U8 *ie,
tANI_U32 limit)
{
tANI_U8 len;
tANI_U8 data;
/* skip white space */
while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command))
{
command++;
limit--;
}
/* skip element id and element length */
len = 2;
/* extract oui */
while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
(limit > 1))
{
/* Convert ASCII to decimal */
data = ((*command -'0') << 4) | (*(command + 1) - '0');
ie[len++] = data;
command += 2;
limit -= 2;
}
/* skip white space */
while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command))
{
command++;
limit--;
}
/* extract data */
while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
(limit > 1))
{
/* Convert ASCII to decimal */
data = ((*command -'0') << 4) | (*(command + 1) - '0');
ie[len++] = data;
command += 2;
limit -= 2;
}
/* fill element id and element length */
ie[0] = IE_EID_VENDOR;
ie[1] = len - 2;
return len;
}
static tANI_U32 hdd_find_ibss_wpa_ie_pos(tANI_U8 *addIePtr, tANI_U32 addIeLen)
{
tANI_U32 ieLenPresent = 0;
int left = addIeLen;
v_U8_t *ptr = addIePtr;
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(LOGE,
FL("****Invalid elem_len=%d left=%d*****"),
elem_len,left);
return 0;
}
if ((elem_id == IE_EID_VENDOR) &&
(left >= WPA_OUI_TYPE_SIZE))
{
if (!memcmp(&ptr[2], WPA_OUI_TYPE,
WPA_OUI_TYPE_SIZE))
{
ieLenPresent += elem_len + 2;
return ieLenPresent;
}
}
ieLenPresent += (elem_len + 2);
left -= elem_len;
ptr += (elem_len + 2);
}
return 0;
}
#endif /* WLAN_FEATURE_RMC */
#ifdef FEATURE_WLAN_BATCH_SCAN
/**---------------------------------------------------------------------------
\brief hdd_extract_assigned_int_from_str() - Extracts assigned integer from
input string
This function extracts assigned integer from string in below format:
"STRING=10" : extracts integer 10 from this string
\param - pInPtr Pointer to input string
\param - base Base for string to int conversion(10 for decimal 16 for hex)
\param - pOutPtr Pointer to variable in which extracted integer needs to be
assigned
\param - pLastArg to tell whether it is last arguement in input string or
not
\return - NULL for failure cases
pointer to next arguement in input string for success cases
--------------------------------------------------------------------------*/
static tANI_U8 *
hdd_extract_assigned_int_from_str
(
tANI_U8 *pInPtr,
tANI_U8 base,
tANI_U32 *pOutPtr,
tANI_U8 *pLastArg
)
{
int tempInt;
int v = 0;
char buf[32];
int val = 0;
*pLastArg = FALSE;
pInPtr = strnchr(pInPtr, strlen(pInPtr), EQUALS_TO_ASCII_VALUE);
if (NULL == pInPtr)
{
return NULL;
}
pInPtr++;
while ((SPACE_ASCII_VALUE == *pInPtr) && ('\0' != *pInPtr)) pInPtr++;
val = sscanf(pInPtr, "%32s ", buf);
if (val < 0 && val > strlen(pInPtr))
{
return NULL;
}
pInPtr += val;
v = kstrtos32(buf, base, &tempInt);
if (v < 0)
{
return NULL;
}
if (tempInt < 0)
{
tempInt = 0;
}
*pOutPtr = tempInt;
pInPtr = strnchr(pInPtr, strlen(pInPtr), SPACE_ASCII_VALUE);
if (NULL == pInPtr)
{
*pLastArg = TRUE;
return NULL;
}
while ((SPACE_ASCII_VALUE == *pInPtr) && ('\0' != *pInPtr)) pInPtr++;
return pInPtr;
}
/**---------------------------------------------------------------------------
\brief hdd_extract_assigned_char_from_str() - Extracts assigned char from
input string
This function extracts assigned character from string in below format:
"STRING=A" : extracts char 'A' from this string
\param - pInPtr Pointer to input string
\param - pOutPtr Pointer to variable in which extracted char needs to be
assigned
\param - pLastArg to tell whether it is last arguement in input string or
not
\return - NULL for failure cases
pointer to next arguement in input string for success cases
--------------------------------------------------------------------------*/
static tANI_U8 *
hdd_extract_assigned_char_from_str
(
tANI_U8 *pInPtr,
tANI_U8 *pOutPtr,
tANI_U8 *pLastArg
)
{
*pLastArg = FALSE;
pInPtr = strnchr(pInPtr, strlen(pInPtr), EQUALS_TO_ASCII_VALUE);
if (NULL == pInPtr)
{
return NULL;
}
pInPtr++;
while ((SPACE_ASCII_VALUE == *pInPtr) && ('\0' != *pInPtr)) pInPtr++;
*pOutPtr = *pInPtr;
pInPtr = strnchr(pInPtr, strlen(pInPtr), SPACE_ASCII_VALUE);
if (NULL == pInPtr)
{
*pLastArg = TRUE;
return NULL;
}
while ((SPACE_ASCII_VALUE == *pInPtr) && ('\0' != *pInPtr)) pInPtr++;
return pInPtr;
}
/**---------------------------------------------------------------------------
\brief hdd_parse_set_batchscan_command () - HDD parse set batch scan command
This function parses set batch scan command in below format:
WLS_BATCHING_SET <space> followed by below arguements
"SCANFREQ=XX" : Optional defaults to 30 sec
"MSCAN=XX" : Required number of scans to attempt to batch
"BESTN=XX" : Best Network (RSSI) defaults to 16
"CHANNEL=<X,Y>" : optional defaults to all channels, can list 'A'or` B.
A. implies only 5 GHz , B. implies only 2.4GHz
"RTT=X" : optional defaults to 0
returns the MIN of MSCAN or the max # of scans firmware can cache or -1 on
error
For example input commands:
1) WLS_BATCHING_SET SCANFREQ=60 MSCAN=10 BESTN=20 CHANNEL=A RTT=0 -> This is
translated into set batch scan with following parameters:
a) Frequence 60 seconds
b) Batch 10 scans together
c) Best RSSI to be 20
d) 5GHz band only
e) RTT is equal to 0
\param - pValue Pointer to input channel list
\param - pHddSetBatchScanReq Pointer to HDD batch scan request structure
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_set_batchscan_command
(
tANI_U8 *pValue,
tSirSetBatchScanReq *pHddSetBatchScanReq
)
{
tANI_U8 *inPtr = pValue;
tANI_U8 val = 0;
tANI_U8 lastArg = 0;
tANI_U32 nScanFreq = HDD_SET_BATCH_SCAN_DEFAULT_FREQ;
tANI_U32 nMscan;
tANI_U32 nBestN = HDD_SET_BATCH_SCAN_BEST_NETWORK;
tANI_U8 ucRfBand = HDD_SET_BATCH_SCAN_DEFAULT_BAND;
tANI_U32 nRtt = 0;
tANI_U32 temp;
/*go to space after WLS_BATCHING_SET command*/
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/*check and parse SCANFREQ*/
if ((strncmp(inPtr, "SCANFREQ", 8) == 0))
{
inPtr = hdd_extract_assigned_int_from_str(inPtr, 10,
&temp, &lastArg);
if (0 != temp)
{
nScanFreq = temp;
}
if ( (NULL == inPtr) || (TRUE == lastArg))
{
return -EINVAL;
}
}
/*check and parse MSCAN*/
if ((strncmp(inPtr, "MSCAN", 5) == 0))
{
inPtr = hdd_extract_assigned_int_from_str(inPtr, 10,
&nMscan, &lastArg);
if (0 == nMscan)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"invalid MSCAN=%d", nMscan);
return -EINVAL;
}
if (TRUE == lastArg)
{
goto done;
}
else if (NULL == inPtr)
{
return -EINVAL;
}
}
else
{
return -EINVAL;
}
/*check and parse BESTN*/
if ((strncmp(inPtr, "BESTN", 5) == 0))
{
inPtr = hdd_extract_assigned_int_from_str(inPtr, 10,
&temp, &lastArg);
if (0 != temp)
{
nBestN = temp;
}
if (TRUE == lastArg)
{
goto done;
}
else if (NULL == inPtr)
{
return -EINVAL;
}
}
/*check and parse CHANNEL*/
if ((strncmp(inPtr, "CHANNEL", 7) == 0))
{
inPtr = hdd_extract_assigned_char_from_str(inPtr, &val, &lastArg);
if (('A' == val) || ('a' == val))
{
ucRfBand = HDD_SET_BATCH_SCAN_5GHz_BAND_ONLY;
}
else if (('B' == val) || ('b' == val))
{
ucRfBand = HDD_SET_BATCH_SCAN_24GHz_BAND_ONLY;
}
else
{
ucRfBand = HDD_SET_BATCH_SCAN_DEFAULT_BAND;
}
if (TRUE == lastArg)
{
goto done;
}
else if (NULL == inPtr)
{
return -EINVAL;
}
}
/*check and parse RTT*/
if ((strncmp(inPtr, "RTT", 3) == 0))
{
inPtr = hdd_extract_assigned_int_from_str(inPtr, 10,
&nRtt, &lastArg);
if (TRUE == lastArg)
{
goto done;
}
if (NULL == inPtr)
{
return -EINVAL;
}
}
done:
pHddSetBatchScanReq->scanFrequency = nScanFreq;
pHddSetBatchScanReq->numberOfScansToBatch = nMscan;
pHddSetBatchScanReq->bestNetwork = nBestN;
pHddSetBatchScanReq->rfBand = ucRfBand;
pHddSetBatchScanReq->rtt = nRtt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Received WLS_BATCHING_SET with SCANFREQ=%d "
"MSCAN=%d BESTN=%d CHANNEL=%d RTT=%d",
pHddSetBatchScanReq->scanFrequency,
pHddSetBatchScanReq->numberOfScansToBatch,
pHddSetBatchScanReq->bestNetwork,
pHddSetBatchScanReq->rfBand,
pHddSetBatchScanReq->rtt);
return 0;
}/*End of hdd_parse_set_batchscan_command*/
/**---------------------------------------------------------------------------
\brief hdd_set_batch_scan_req_callback () - This function is called after
receiving set batch scan response from FW and it saves set batch scan
response data FW to HDD context and sets the completion event on
which hdd_ioctl is waiting
\param - callbackContext Pointer to HDD adapter
\param - pRsp Pointer to set batch scan response data received from FW
\return - nothing
--------------------------------------------------------------------------*/
static void hdd_set_batch_scan_req_callback
(
void *callbackContext,
tSirSetBatchScanRsp *pRsp
)
{
hdd_adapter_t* pAdapter = (hdd_adapter_t*)callbackContext;
tSirSetBatchScanRsp *pHddSetBatchScanRsp;
/*sanity check*/
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid pAdapter magic", __func__);
VOS_ASSERT(0);
return;
}
pHddSetBatchScanRsp = &pAdapter->hddSetBatchScanRsp;
/*save set batch scan response*/
pHddSetBatchScanRsp->nScansToBatch = pRsp->nScansToBatch;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"Received set batch scan rsp from FW with nScansToBatch=%d",
pHddSetBatchScanRsp->nScansToBatch);
pAdapter->hdd_wait_for_set_batch_scan_rsp = FALSE;
complete(&pAdapter->hdd_set_batch_scan_req_var);
return;
}/*End of hdd_set_batch_scan_req_callback*/
/**---------------------------------------------------------------------------
\brief hdd_populate_batch_scan_rsp_queue () - This function stores AP meta
info in hdd batch scan response queue
\param - pAdapter Pointer to hdd adapter
\param - pAPMetaInfo Pointer to access point meta info
\param - scanId scan ID of batch scan response
\param - isLastAp tells whether AP is last AP in batch scan response or not
\return - nothing
--------------------------------------------------------------------------*/
static void hdd_populate_batch_scan_rsp_queue( hdd_adapter_t* pAdapter,
tpSirBatchScanNetworkInfo pApMetaInfo, tANI_U32 scanId, v_BOOL_t isLastAp)
{
tHddBatchScanRsp *pHead;
tHddBatchScanRsp *pNode;
tHddBatchScanRsp *pPrev;
tHddBatchScanRsp *pTemp;
tANI_U8 ssidLen;
/*head of hdd batch scan response queue*/
pHead = pAdapter->pBatchScanRsp;
pNode = (tHddBatchScanRsp *)vos_mem_malloc(sizeof(tHddBatchScanRsp));
if (NULL == pNode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Could not allocate memory", __func__);
VOS_ASSERT(0);
return;
}
vos_mem_copy(pNode->ApInfo.bssid, pApMetaInfo->bssid,
sizeof(pNode->ApInfo.bssid));
ssidLen = strlen(pApMetaInfo->ssid);
if (SIR_MAX_SSID_SIZE < ssidLen)
{
/*invalid scan result*/
vos_mem_free(pNode);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid AP meta info ssidlen %d", __func__, ssidLen);
return;
}
vos_mem_copy(pNode->ApInfo.ssid, pApMetaInfo->ssid, ssidLen);
/*null terminate ssid*/
pNode->ApInfo.ssid[ssidLen] = '\0';
pNode->ApInfo.ch = pApMetaInfo->ch;
pNode->ApInfo.rssi = pApMetaInfo->rssi;
pNode->ApInfo.age = pApMetaInfo->timestamp;
pNode->ApInfo.batchId = scanId;
pNode->ApInfo.isLastAp = isLastAp;
pNode->pNext = NULL;
if (NULL == pHead)
{
pAdapter->pBatchScanRsp = pNode;
}
else
{
pTemp = pHead;
while (NULL != pTemp)
{
pPrev = pTemp;
pTemp = pTemp->pNext;
}
pPrev->pNext = pNode;
}
return;
}/*End of hdd_populate_batch_scan_rsp_queue*/
/**---------------------------------------------------------------------------
\brief hdd_batch_scan_result_ind_callback () - This function is called after
receiving batch scan response indication from FW. It saves get batch scan
response data in HDD batch scan response queue. This callback sets the
completion event on which hdd_ioctl is waiting only after getting complete
batch scan response data from FW
\param - callbackContext Pointer to HDD adapter
\param - pRsp Pointer to get batch scan response data received from FW
\return - nothing
--------------------------------------------------------------------------*/
static void hdd_batch_scan_result_ind_callback
(
void *callbackContext,
void *pRsp
)
{
v_BOOL_t isLastAp;
tANI_U32 numApMetaInfo;
tANI_U32 numNetworkInScanList;
tANI_U32 numberScanList;
tANI_U32 nextScanListOffset;
tANI_U32 nextApMetaInfoOffset;
hdd_adapter_t* pAdapter;
tpSirBatchScanList pScanList;
tpSirBatchScanNetworkInfo pApMetaInfo;
tpSirBatchScanResultIndParam pBatchScanRsp;/*batch scan rsp data from FW*/
tSirSetBatchScanReq *pReq;
pAdapter = (hdd_adapter_t *)callbackContext;
/*sanity check*/
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid pAdapter magic", __func__);
VOS_ASSERT(0);
return;
}
/*initialize locals*/
pReq = &pAdapter->hddSetBatchScanReq;
pBatchScanRsp = (tpSirBatchScanResultIndParam)pRsp;
isLastAp = FALSE;
numApMetaInfo = 0;
numNetworkInScanList = 0;
numberScanList = 0;
nextScanListOffset = 0;
nextApMetaInfoOffset = 0;
pScanList = NULL;
pApMetaInfo = NULL;
if ((NULL == pBatchScanRsp) || (NULL == pReq))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pBatchScanRsp is %pK pReq %pK", __func__, pBatchScanRsp, pReq);
isLastAp = TRUE;
goto done;
}
pAdapter->numScanList = numberScanList = pBatchScanRsp->numScanLists;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Batch scan rsp: numberScalList %d", numberScanList);
if ((!numberScanList) || (numberScanList > pReq->numberOfScansToBatch))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: numberScanList %d", __func__, numberScanList);
isLastAp = TRUE;
goto done;
}
while (numberScanList)
{
pScanList = (tpSirBatchScanList)((tANI_U8 *)pBatchScanRsp->scanResults +
nextScanListOffset);
if (NULL == pScanList)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pScanList is %pK", __func__, pScanList);
isLastAp = TRUE;
goto done;
}
numNetworkInScanList = numApMetaInfo = pScanList->numNetworksInScanList;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Batch scan rsp: numApMetaInfo %d scanId %d",
numApMetaInfo, pScanList->scanId);
if ((!numApMetaInfo) || (numApMetaInfo > pReq->bestNetwork))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: numApMetaInfo %d", __func__, numApMetaInfo);
isLastAp = TRUE;
goto done;
}
/*Initialize next AP meta info offset for next scan list*/
nextApMetaInfoOffset = 0;
while (numApMetaInfo)
{
pApMetaInfo = (tpSirBatchScanNetworkInfo)(pScanList->scanList +
nextApMetaInfoOffset);
if (NULL == pApMetaInfo)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: pApMetaInfo is %pK", __func__, pApMetaInfo);
isLastAp = TRUE;
goto done;
}
/*calculate AP age*/
pApMetaInfo->timestamp =
pBatchScanRsp->timestamp - pApMetaInfo->timestamp;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"%s: bssId "MAC_ADDRESS_STR
" ch %d rssi %d timestamp %d", __func__,
MAC_ADDR_ARRAY(pApMetaInfo->bssid),
pApMetaInfo->ch, pApMetaInfo->rssi,
pApMetaInfo->timestamp);
/*mark last AP in batch scan response*/
if ((TRUE == pBatchScanRsp->isLastResult) &&
(1 == numberScanList) && (1 == numApMetaInfo))
{
isLastAp = TRUE;
}
mutex_lock(&pAdapter->hdd_batch_scan_lock);
/*store batch scan repsonse in hdd queue*/
hdd_populate_batch_scan_rsp_queue(pAdapter, pApMetaInfo,
pScanList->scanId, isLastAp);
mutex_unlock(&pAdapter->hdd_batch_scan_lock);
nextApMetaInfoOffset += sizeof(tSirBatchScanNetworkInfo);
numApMetaInfo--;
}
nextScanListOffset += ((sizeof(tSirBatchScanList) - sizeof(tANI_U8))
+ (sizeof(tSirBatchScanNetworkInfo)
* numNetworkInScanList));
numberScanList--;
}
done:
/*notify hdd_ioctl only if complete batch scan rsp is received and it was
requested from hdd_ioctl*/
if ((TRUE == pAdapter->hdd_wait_for_get_batch_scan_rsp) &&
(TRUE == isLastAp))
{
pAdapter->hdd_wait_for_get_batch_scan_rsp = FALSE;
complete(&pAdapter->hdd_get_batch_scan_req_var);
}
return;
}/*End of hdd_batch_scan_result_ind_callback*/
/**---------------------------------------------------------------------------
\brief hdd_format_batch_scan_rsp () - This function formats batch scan
response as per batch scan FR request format by putting proper markers
\param - pDest pointer to destination buffer
\param - cur_len current length
\param - tot_len total remaining size which can be written to user space
\param - pApMetaInfo Pointer to get batch scan response AP meta info
\param - pAdapter Pointer to HDD adapter
\return - ret no of characters written
--------------------------------------------------------------------------*/
static tANI_U32
hdd_format_batch_scan_rsp
(
tANI_U8 *pDest,
tANI_U32 cur_len,
tANI_U32 tot_len,
tHddBatchScanRsp *pApMetaInfo,
hdd_adapter_t* pAdapter
)
{
tANI_U32 ret = 0;
tANI_U32 rem_len = 0;
tANI_U8 temp_len = 0;
tANI_U8 temp_total_len = 0;
tANI_U8 temp[HDD_BATCH_SCAN_AP_META_INFO_SIZE];
tANI_U8 *pTemp = temp;
/*Batch scan reponse needs to be returned to user space in
following format:
"scancount=X\n" where X is the number of scans in current batch
batch
"trunc\n" optional present if current scan truncated
"bssid=XX:XX:XX:XX:XX:XX\n"
"ssid=XXXX\n"
"freq=X\n" frequency in Mhz
"level=XX\n"
"age=X\n" ms
"dist=X\n" cm (-1 if not available)
"errror=X\n" (-1if not available)
"====\n" (end of ap marker)
"####\n" (end of scan marker)
"----\n" (end of results)*/
/*send scan result in above format to user space based on
available length*/
/*The GET response may have more data than the driver can return in its
buffer. In that case the buffer should be filled to the nearest complete
scan, ending with "%%%%".Subsequent callsshould return the remaining data
starting with the next scan (optional .trunc\n., .apcount=X\n., etc).
The final buffer should end with "----\n"*/
/*sanity*/
if (cur_len > tot_len)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: invaid cur_len %d tot_len %d", __func__, cur_len, tot_len);
return 0;
}
else
{
rem_len = (tot_len - cur_len);
}
/*end scan marker*/
if (pApMetaInfo->ApInfo.batchId != pAdapter->prev_batch_id)
{
temp_len = snprintf(pTemp, sizeof(temp), "####\n");
pTemp += temp_len;
temp_total_len += temp_len;
}
/*bssid*/
temp_len = snprintf(pTemp, sizeof(temp),
"bssid=0x%x:0x%x:0x%x:0x%x:0x%x:0x%x\n",
pApMetaInfo->ApInfo.bssid[0], pApMetaInfo->ApInfo.bssid[1],
pApMetaInfo->ApInfo.bssid[2], pApMetaInfo->ApInfo.bssid[3],
pApMetaInfo->ApInfo.bssid[4], pApMetaInfo->ApInfo.bssid[5]);
pTemp += temp_len;
temp_total_len += temp_len;
/*ssid*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "ssid=%s\n",
pApMetaInfo->ApInfo.ssid);
pTemp += temp_len;
temp_total_len += temp_len;
/*freq*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "freq=%d\n",
sme_ChnToFreq(pApMetaInfo->ApInfo.ch));
pTemp += temp_len;
temp_total_len += temp_len;
/*level*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "level=%d\n",
pApMetaInfo->ApInfo.rssi);
pTemp += temp_len;
temp_total_len += temp_len;
/*age*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "age=%d\n",
pApMetaInfo->ApInfo.age);
pTemp += temp_len;
temp_total_len += temp_len;
/*dist*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "dist=-1\n");
pTemp += temp_len;
temp_total_len += temp_len;
/*error*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "error=-1\n");
pTemp += temp_len;
temp_total_len += temp_len;
/*end AP marker*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "====\n");
pTemp += temp_len;
temp_total_len += temp_len;
/*last AP in batch scan response*/
if(TRUE == pApMetaInfo->ApInfo.isLastAp)
{
/*end scan marker*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "####\n");
pTemp += temp_len;
temp_total_len += temp_len;
/*end batch scan result marker*/
temp_len = snprintf(pTemp, (sizeof(temp) - temp_total_len), "----\n");
pTemp += temp_len;
temp_total_len += temp_len;
}
if (temp_total_len < rem_len)
{
ret = temp_total_len + 1;
strlcpy(pDest, temp, ret);
pAdapter->isTruncated = FALSE;
}
else
{
pAdapter->isTruncated = TRUE;
if (rem_len >= strlen("%%%%"))
{
ret = snprintf(pDest, sizeof(temp), "%%%%");
}
else
{
ret = 0;
}
}
return ret;
}/*End of hdd_format_batch_scan_rsp*/
/**---------------------------------------------------------------------------
\brief hdd_populate_user_batch_scan_rsp() - This function populates user data
buffer starting with head of hdd batch scan response queue
\param - pAdapter Pointer to HDD adapter
\param - pDest Pointer to user data buffer
\param - cur_len current offset in user buffer
\param - rem_len remaining no of bytes in user buffer
\return - number of bytes written in user buffer
--------------------------------------------------------------------------*/
tANI_U32 hdd_populate_user_batch_scan_rsp
(
hdd_adapter_t* pAdapter,
tANI_U8 *pDest,
tANI_U32 cur_len,
tANI_U32 rem_len
)
{
tHddBatchScanRsp *pHead;
tHddBatchScanRsp *pPrev;
tANI_U32 len;
pAdapter->isTruncated = FALSE;
/*head of hdd batch scan response queue*/
pHead = pAdapter->pBatchScanRsp;
while (pHead)
{
len = hdd_format_batch_scan_rsp(pDest, cur_len, rem_len, pHead,
pAdapter);
pDest += len;
pDest--;
cur_len += len;
if(TRUE == pAdapter->isTruncated)
{
/*result is truncated return rest of scan rsp in next req*/
cur_len = rem_len;
break;
}
pPrev = pHead;
pHead = pHead->pNext;
pAdapter->pBatchScanRsp = pHead;
if (TRUE == pPrev->ApInfo.isLastAp)
{
pAdapter->prev_batch_id = 0;
}
else
{
pAdapter->prev_batch_id = pPrev->ApInfo.batchId;
}
vos_mem_free(pPrev);
pPrev = NULL;
}
return cur_len;
}/*End of hdd_populate_user_batch_scan_rsp*/
/**---------------------------------------------------------------------------
\brief hdd_return_batch_scan_rsp_to_user () - This function returns batch
scan response data from HDD queue to user space
It does following in detail:
a) if HDD has enough data in its queue then it 1st copies data to user
space and then send get batch scan indication message to FW. In this
case it does not wait on any event and batch scan response data will
be populated in HDD response queue in MC thread context after receiving
indication from FW
b) else send get batch scan indication message to FW and wait on an event
which will be set once HDD receives complete batch scan response from
FW and then this function returns batch scan response to user space
\param - pAdapter Pointer to HDD adapter
\param - pPrivData Pointer to priv_data
\return - 0 for success -EFAULT for failure
--------------------------------------------------------------------------*/
int hdd_return_batch_scan_rsp_to_user
(
hdd_adapter_t* pAdapter,
hdd_priv_data_t *pPrivData,
tANI_U8 *command
)
{
tANI_U8 *pDest;
tANI_U32 count = 0;
tANI_U32 len = 0;
tANI_U32 cur_len = 0;
tANI_U32 rem_len = 0;
eHalStatus halStatus;
unsigned long rc;
tSirTriggerBatchScanResultInd *pReq;
pReq = &pAdapter->hddTriggerBatchScanResultInd;
pReq->param = 0;/*batch scan client*/
pDest = (tANI_U8 *)(command + pPrivData->used_len);
pAdapter->hdd_wait_for_get_batch_scan_rsp = FALSE;
cur_len = pPrivData->used_len;
if (pPrivData->total_len > pPrivData->used_len)
{
rem_len = pPrivData->total_len - pPrivData->used_len;
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid user data buffer total_len %d used_len %d",
__func__, pPrivData->total_len, pPrivData->used_len);
return -EFAULT;
}
mutex_lock(&pAdapter->hdd_batch_scan_lock);
len = hdd_populate_user_batch_scan_rsp(pAdapter, pDest,
cur_len, rem_len);
mutex_unlock(&pAdapter->hdd_batch_scan_lock);
/*enough scan result available in cache to return to user space or
scan result needs to be fetched 1st from fw and then return*/
if (len == cur_len)
{
pAdapter->hdd_wait_for_get_batch_scan_rsp = TRUE;
halStatus = sme_TriggerBatchScanResultInd(
WLAN_HDD_GET_HAL_CTX(pAdapter), pReq,
pAdapter->sessionId, hdd_batch_scan_result_ind_callback,
pAdapter);
if ( eHAL_STATUS_SUCCESS == halStatus )
{
if (TRUE == pAdapter->hdd_wait_for_get_batch_scan_rsp)
{
INIT_COMPLETION(pAdapter->hdd_get_batch_scan_req_var);
rc = wait_for_completion_timeout(
&pAdapter->hdd_get_batch_scan_req_var,
msecs_to_jiffies(HDD_GET_BATCH_SCAN_RSP_TIME_OUT));
if (0 >= rc)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: wait on hdd_get_batch_scan_req_var failed %ld",
__func__, rc);
return -EFAULT;
}
}
len = snprintf(pDest, HDD_BATCH_SCAN_AP_META_INFO_SIZE,
"scancount=%u\n", pAdapter->numScanList);
pDest += len;
cur_len += len;
mutex_lock(&pAdapter->hdd_batch_scan_lock);
len = hdd_populate_user_batch_scan_rsp(pAdapter, pDest,
cur_len, rem_len);
mutex_unlock(&pAdapter->hdd_batch_scan_lock);
count = 0;
len = (len - pPrivData->used_len);
pDest = (command + pPrivData->used_len);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"NEW BATCH SCAN RESULT:");
while(count < len)
{
printk("%c", *(pDest + count));
count++;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: copy %d data to user buffer", __func__, len);
if (copy_to_user(pPrivData->buf, pDest, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
return -EFAULT;
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"sme_GetBatchScanScan returned failure halStatus %d",
halStatus);
return -EINVAL;
}
}
else
{
count = 0;
len = (len - pPrivData->used_len);
pDest = (command + pPrivData->used_len);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"REMAINING TRUNCATED BATCH SCAN RESULT:");
while(count < len)
{
printk("%c", *(pDest + count));
count++;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: copy %d data to user buffer", __func__, len);
if (copy_to_user(pPrivData->buf, pDest, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
return -EFAULT;
}
}
return 0;
} /*End of hdd_return_batch_scan_rsp_to_user*/
/**---------------------------------------------------------------------------
\brief hdd_handle_batch_scan_ioctl () - This function handles WLS_BATCHING
IOCTLs from user space. Following BATCH SCAN DEV IOCTs are handled:
WLS_BATCHING VERSION
WLS_BATCHING SET
WLS_BATCHING GET
WLS_BATCHING STOP
\param - pAdapter Pointer to HDD adapter
\param - pPrivdata Pointer to priv_data
\param - command Pointer to command
\return - 0 for success -EFAULT for failure
--------------------------------------------------------------------------*/
int hdd_handle_batch_scan_ioctl
(
hdd_adapter_t *pAdapter,
hdd_priv_data_t *pPrivdata,
tANI_U8 *command
)
{
int ret = 0;
hdd_context_t *pHddCtx;
ENTER();
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (ret)
{
goto exit;
}
if (strncmp(command, "WLS_BATCHING VERSION", 20) == 0)
{
char extra[32];
tANI_U8 len = 0;
tANI_U8 version = HDD_BATCH_SCAN_VERSION;
if (FALSE == sme_IsFeatureSupportedByFW(BATCH_SCAN))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Batch scan feature is not supported by FW", __func__);
ret = -EINVAL;
goto exit;
}
len = scnprintf(extra, sizeof(extra), "WLS_BATCHING_VERSION %d",
version);
if (copy_to_user(pPrivdata->buf, &extra, len + 1))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
ret = HDD_BATCH_SCAN_VERSION;
}
else if (strncmp(command, "WLS_BATCHING SET", 16) == 0)
{
int status;
tANI_U8 *value = (command + 16);
eHalStatus halStatus;
unsigned long rc;
tSirSetBatchScanReq *pReq = &pAdapter->hddSetBatchScanReq;
tSirSetBatchScanRsp *pRsp = &pAdapter->hddSetBatchScanRsp;
if (FALSE == sme_IsFeatureSupportedByFW(BATCH_SCAN))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Batch scan feature is not supported by FW", __func__);
ret = -EINVAL;
goto exit;
}
if ((WLAN_HDD_INFRA_STATION != pAdapter->device_mode) &&
(WLAN_HDD_P2P_CLIENT != pAdapter->device_mode) &&
(WLAN_HDD_P2P_GO != pAdapter->device_mode) &&
(WLAN_HDD_P2P_DEVICE != pAdapter->device_mode))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Received WLS_BATCHING SET command in invalid mode %s (%d) "
"WLS_BATCHING_SET is only allowed in infra STA/P2P client mode",
hdd_device_modetoString(pAdapter->device_mode),
pAdapter->device_mode);
ret = -EINVAL;
goto exit;
}
status = hdd_parse_set_batchscan_command(value, pReq);
if (status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid WLS_BATCHING SET command");
ret = -EINVAL;
goto exit;
}
pAdapter->hdd_wait_for_set_batch_scan_rsp = TRUE;
halStatus = sme_SetBatchScanReq(WLAN_HDD_GET_HAL_CTX(pAdapter), pReq,
pAdapter->sessionId, hdd_set_batch_scan_req_callback,
pAdapter);
if ( eHAL_STATUS_SUCCESS == halStatus )
{
char extra[32];
tANI_U8 len = 0;
tANI_U8 mScan = 0;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"sme_SetBatchScanReq returned success halStatus %d",
halStatus);
if (TRUE == pAdapter->hdd_wait_for_set_batch_scan_rsp)
{
INIT_COMPLETION(pAdapter->hdd_set_batch_scan_req_var);
rc = wait_for_completion_timeout(
&pAdapter->hdd_set_batch_scan_req_var,
msecs_to_jiffies(HDD_SET_BATCH_SCAN_REQ_TIME_OUT));
if (0 == rc)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Timeout waiting for set batch scan to complete",
__func__);
ret = -EINVAL;
goto exit;
}
}
if ( !pRsp->nScansToBatch )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Received set batch scan failure response from FW",
__func__);
ret = -EINVAL;
goto exit;
}
/*As per the Batch Scan Framework API we should return the MIN of
either MSCAN or the max # of scans firmware can cache*/
mScan = MIN(pReq->numberOfScansToBatch , pRsp->nScansToBatch);
pAdapter->batchScanState = eHDD_BATCH_SCAN_STATE_STARTED;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: request MSCAN %d response MSCAN %d ret %d",
__func__, pReq->numberOfScansToBatch, pRsp->nScansToBatch, mScan);
len = scnprintf(extra, sizeof(extra), "%d", mScan);
if (copy_to_user(pPrivdata->buf, &extra, len + 1))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy MSCAN value to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"sme_SetBatchScanReq returned failure halStatus %d",
halStatus);
ret = -EINVAL;
goto exit;
}
}
else if (strncmp(command, "WLS_BATCHING STOP", 17) == 0)
{
eHalStatus halStatus;
tSirStopBatchScanInd *pInd = &pAdapter->hddStopBatchScanInd;
pInd->param = 0;
if (FALSE == sme_IsFeatureSupportedByFW(BATCH_SCAN))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Batch scan feature is not supported by FW", __func__);
ret = -EINVAL;
goto exit;
}
if (eHDD_BATCH_SCAN_STATE_STARTED != pAdapter->batchScanState)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"Batch scan is not yet enabled batch scan state %d",
pAdapter->batchScanState);
ret = -EINVAL;
goto exit;
}
mutex_lock(&pAdapter->hdd_batch_scan_lock);
hdd_deinit_batch_scan(pAdapter);
mutex_unlock(&pAdapter->hdd_batch_scan_lock);
pAdapter->batchScanState = eHDD_BATCH_SCAN_STATE_STOPPED;
halStatus = sme_StopBatchScanInd(WLAN_HDD_GET_HAL_CTX(pAdapter), pInd,
pAdapter->sessionId);
if ( eHAL_STATUS_SUCCESS == halStatus )
{
ret = 0;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"sme_StopBatchScanInd returned success halStatus %d",
halStatus);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"sme_StopBatchScanInd returned failure halStatus %d",
halStatus);
ret = -EINVAL;
goto exit;
}
}
else if (strncmp(command, "WLS_BATCHING GET", 16) == 0)
{
tANI_U32 remain_len;
if (FALSE == sme_IsFeatureSupportedByFW(BATCH_SCAN))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Batch scan feature is not supported by FW", __func__);
ret = -EINVAL;
goto exit;
}
if (eHDD_BATCH_SCAN_STATE_STARTED != pAdapter->batchScanState)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"Batch scan is not yet enabled could not return results"
"Batch Scan state %d",
pAdapter->batchScanState);
ret = -EINVAL;
goto exit;
}
pPrivdata->used_len = 16;
remain_len = pPrivdata->total_len - pPrivdata->used_len;
if (remain_len < pPrivdata->total_len)
{
/*Clear previous batch scan response data if any*/
vos_mem_zero((tANI_U8 *)(command + pPrivdata->used_len), remain_len);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid total length from user space can't fetch batch"
" scan response total_len %d used_len %d remain len %d",
pPrivdata->total_len, pPrivdata->used_len, remain_len);
ret = -EINVAL;
goto exit;
}
ret = hdd_return_batch_scan_rsp_to_user(pAdapter, pPrivdata, command);
}
exit:
EXIT();
return ret;
}
#endif/*End of FEATURE_WLAN_BATCH_SCAN*/
#if defined(WLAN_FEATURE_VOWIFI_11R) || defined(FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
/**
* hdd_assign_handoff_src_reassoc - Set handoff source as REASSOC
* to Handoff request
* @handoffInfo: Pointer to Handoff request
* @src: enum of handoff_src
* Return: None
*/
#ifndef QCA_WIFI_ISOC
static inline void hdd_assign_handoff_src_reassoc(tCsrHandoffRequest
*handoffInfo, handoff_src src)
{
handoffInfo->src = src;
}
#else
static inline void hdd_assign_handoff_src_reassoc(tCsrHandoffRequest
*handoffInfo, handoff_src src)
{
}
#endif
/**
* hdd_reassoc() - perform a user space-directed reassoc
*
* @pAdapter: Adapter upon which the command was received
* @bssid: BSSID with which to reassociate
* @channel: channel upon which to reassociate
* @src: The source for the trigger of this action
*
* Return: 0 for success non-zero for failure
*/
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
int hdd_reassoc(hdd_adapter_t *pAdapter, const tANI_U8 *bssid,
const tANI_U8 channel, const handoff_src src)
{
hdd_station_ctx_t *pHddStaCtx;
tCsrHandoffRequest handoffInfo;
hdd_context_t *pHddCtx = NULL;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* if not associated, no need to proceed with reassoc */
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
hddLog(LOG1, FL("Not associated"));
return -EINVAL;
}
/* if the target bssid is same as currently associated AP,
then no need to proceed with reassoc */
if (!memcmp(bssid, pHddStaCtx->conn_info.bssId, sizeof(tSirMacAddr))) {
hddLog(LOG1, FL("Reassoc BSSID is same as currently associated AP bssid"));
return -EINVAL;
}
/* Check channel number is a valid channel number */
if (VOS_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(pAdapter, channel)) {
hddLog(LOGE, FL("Invalid Channel %d"), channel);
return -EINVAL;
}
/* Proceed with reassoc */
handoffInfo.channel = channel;
hdd_assign_handoff_src_reassoc(&handoffInfo, src);
memcpy(handoffInfo.bssid, bssid, sizeof(tSirMacAddr));
sme_HandoffRequest(pHddCtx->hHal, &handoffInfo);
return 0;
}
#else
int hdd_reassoc(hdd_adapter_t *pAdapter, const tANI_U8 *bssid,
const tANI_U8 channel, const handoff_src src)
{
return -EPERM;
}
#endif
/**
* hdd_parse_reassoc_v1() - parse version 1 of the REASSOC command
* This function parses the v1 REASSOC command with the format
* REASSOC xx:xx:xx:xx:xx:xx CH where "xx:xx:xx:xx:xx:xx" is the
* Hex-ASCII representation of the BSSID and CH is the ASCII
* representation of the channel. For example
* REASSOC 00:0a:0b:11:22:33 48
*
* @pAdapter: Adapter upon which the command was received
* @command: ASCII text command that was received
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_reassoc_v1(hdd_adapter_t *pAdapter, const char *command)
{
tANI_U8 channel = 0;
tSirMacAddr bssid;
int ret;
ret = hdd_parse_reassoc_command_v1_data(command, bssid, &channel);
if (ret)
hddLog(LOGE, FL("Failed to parse reassoc command data"));
else
ret = hdd_reassoc(pAdapter, bssid, channel, REASSOC);
return ret;
}
/**
* hdd_parse_reassoc_v2() - parse version 2 of the REASSOC command
* This function parses the v2 REASSOC command with the format
* REASSOC <android_wifi_reassoc_params>
*
* @pAdapter: Adapter upon which the command was received
* @command: command that was received, ASCII command followed
* by binary data
* @total_len: Total length of the command received
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_reassoc_v2(hdd_adapter_t *pAdapter, const char *command,
int total_len)
{
struct android_wifi_reassoc_params params;
tSirMacAddr bssid;
int ret;
if (total_len < sizeof(params) + 8) {
hddLog(LOGE, FL("Invalid command length"));
return -EINVAL;
}
/* The params are located after "REASSOC " */
memcpy(&params, command + 8, sizeof(params));
if (!mac_pton(params.bssid, (u8 *)&bssid)) {
hddLog(LOGE, FL("MAC address parsing failed"));
ret = -EINVAL;
} else {
ret = hdd_reassoc(pAdapter, bssid, params.channel, REASSOC);
}
return ret;
}
/**
* hdd_parse_reassoc() - parse the REASSOC command
* There are two different versions of the REASSOC command.Version 1
* of the command contains a parameter list that is ASCII characters
* whereas version 2 contains a combination of ASCII and binary
* payload. Determine if a version 1 or a version 2 command is being
* parsed by examining the parameters, and then dispatch the parser
* that is appropriate for the command.
*
* @pAdapter: Adapter upon which the command was received
* @command: command that was received
* @total_len: Total length of the command received
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_reassoc(hdd_adapter_t *pAdapter, const char *command, int total_len)
{
int ret;
/*
* both versions start with "REASSOC"
* v1 has a bssid and channel # as an ASCII string
* REASSOC xx:xx:xx:xx:xx:xx CH
* v2 has a C struct
* REASSOC <binary c struct>
*
* The first field in the v2 struct is also the bssid in ASCII.
* But in the case of a v2 message the BSSID is NUL-terminated.
* Hence we can peek at that offset to see if this is V1 or V2
* REASSOC xx:xx:xx:xx:xx:xx*
* 1111111111222222
* 01234567890123456789012345
*/
if (total_len < 26) {
hddLog(LOGE, FL("Invalid command (total_len=%d)"), total_len);
return -EINVAL;
}
if (command[25])
ret = hdd_parse_reassoc_v1(pAdapter, command);
else
ret = hdd_parse_reassoc_v2(pAdapter, command, total_len);
return ret;
}
#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE FEATURE_WLAN_LFR */
struct bcn_miss_rate_priv {
int bcn_miss_rate;
};
/**
* get_bcn_miss_rate_cb() callback invoked on receiving beacon miss
* rate from firmware
* @status: Status of get beacon miss rate operation
* @bcnMissRate: Beacon miss rate
* @context: Context passed while registering callback
*
* This function is invoked by WDA layer on receiving
* WDI_GET_BCN_MISS_RATE_RSP
*
* Return: None
*/
static void get_bcn_miss_rate_cb(VOS_STATUS status, int bcnMissRate,
void *context)
{
struct hdd_request *request;
struct bcn_miss_rate_priv *priv;
request = hdd_request_get(context);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
return;
}
priv = hdd_request_priv(request);
if (VOS_STATUS_SUCCESS == status)
priv->bcn_miss_rate = bcnMissRate;
else
hddLog(VOS_TRACE_LEVEL_ERROR, FL("failed to get bcnMissRate"));
hdd_request_complete(request);
hdd_request_put(request);
return;
}
struct fw_stats_priv {
tSirFwStatsResult *fw_stats;
};
/**
* hdd_fw_stats_cb() callback invoked on receiving firmware stats
* from firmware
* @status: Status of get firmware stats operation
* @fwStatsResult: firmware stats
* @context: Context passed while registering callback
*
* This function is invoked by WDA layer on receiving
* WDI_GET_FW_STATS_RSP
*
* Return: None
*/
static void hdd_fw_stats_cb(VOS_STATUS status,
tSirFwStatsResult *fwStatsResult, void *context)
{
struct hdd_request *request;
struct fw_stats_priv *priv;
hddLog(VOS_TRACE_LEVEL_INFO, FL("with status = %d"),status);
request = hdd_request_get(context);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
return;
}
priv = hdd_request_priv(request);
if (VOS_STATUS_SUCCESS == status)
*priv->fw_stats = *fwStatsResult;
else
priv->fw_stats = NULL;
hdd_request_complete(request);
hdd_request_put(request);
return;
}
/*
*hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
*@pValue Pointer to MAXTXPOWER command
*@pTxPower Pointer to tx power
*
*This function parses the MAXTXPOWER command passed in the format
* MAXTXPOWER<space>X(Tx power in dbm)
* For example input commands:
* 1) MAXTXPOWER -8 -> This is translated into set max TX power to -8 dbm
* 2) MAXTXPOWER -23 -> This is translated into set max TX power to -23 dbm
*
*return - 0 for success non-zero for failure
*/
static int hdd_parse_setmaxtxpower_command(unsigned char *pValue, int *pTxPower)
{
unsigned char *inPtr = pValue;
int tempInt;
int v = 0;
*pTxPower = 0;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/* no argument after the command */
if (NULL == inPtr)
return -EINVAL;
/* no space after the command */
else if (SPACE_ASCII_VALUE != *inPtr)
return -EINVAL;
/* removing empty spaces */
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/* no argument followed by spaces */
if ('\0' == *inPtr)
return 0;
v = kstrtos32(inPtr, 10, &tempInt);
/* Range checking for passed parameter */
if ((tempInt < HDD_MIN_TX_POWER) || (tempInt > HDD_MAX_TX_POWER))
return -EINVAL;
*pTxPower = tempInt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"SETMAXTXPOWER: %d", *pTxPower);
return 0;
}
static int hdd_get_dwell_time(hdd_config_t *pCfg, tANI_U8 *command, char *extra, tANI_U8 n, tANI_U8 *len)
{
int ret = 0;
if (!pCfg || !command || !extra || !len)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passsed for GETDWELLTIME is incorrect", __func__);
ret = -EINVAL;
return ret;
}
if (strncmp(command, "GETDWELLTIME ACTIVE MAX", 23) == 0)
{
*len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MAX %u\n",
(int)pCfg->nActiveMaxChnTime);
return ret;
}
else if (strncmp(command, "GETDWELLTIME ACTIVE MIN", 23) == 0)
{
*len = scnprintf(extra, n, "GETDWELLTIME ACTIVE MIN %u\n",
(int)pCfg->nActiveMinChnTime);
return ret;
}
else if (strncmp(command, "GETDWELLTIME PASSIVE MAX", 24) == 0)
{
*len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MAX %u\n",
(int)pCfg->nPassiveMaxChnTime);
return ret;
}
else if (strncmp(command, "GETDWELLTIME PASSIVE MIN", 24) == 0)
{
*len = scnprintf(extra, n, "GETDWELLTIME PASSIVE MIN %u\n",
(int)pCfg->nPassiveMinChnTime);
return ret;
}
else if (strncmp(command, "GETDWELLTIME", 12) == 0)
{
*len = scnprintf(extra, n, "GETDWELLTIME %u \n",
(int)pCfg->nActiveMaxChnTime);
return ret;
}
else
{
ret = -EINVAL;
}
return ret;
}
/**
* hdd_btc_get_dwell_time() - Get BTC dwell time parameters
* @pCfg: Pointer to HDD context
* @command: ASCII text command that is received
* @extra: Pointer to copy data sent to user
* @n: size of 'extra' buffer
* @len: length copied to 'extra' buffer
*
* Driver commands:
* wpa_cli DRIVER BTCGETDWELLTIME ESCO MAX
* wpa_cli DRIVER BTCGETDWELLTIME ESCO MIN
* wpa_cli DRIVER BTCGETDWELLTIME SCO MAX
* wpa_cli DRIVER BTCGETDWELLTIME SCO MIN
*
* Return: 0 for success non-zero for failure
*/
static int hdd_btc_get_dwell_time(hdd_config_t *pCfg, tANI_U8 *command,
char *extra, tANI_U8 n, tANI_U8 *len)
{
int ret = 0;
if (!pCfg || !command || !extra || !len)
{
hddLog(LOGE, FL("Argument passsed for BTCGETDWELLTIME is incorrect"));
ret = -EINVAL;
return ret;
}
if (strncmp(command, "BTCGETDWELLTIME ESCO MAX", 24) == 0)
{
*len = scnprintf(extra, n, "BTCGETDWELLTIME ESCO MAX %u\n",
(int)pCfg->max_chntime_btc_esco);
return ret;
}
else if (strncmp(command, "BTCGETDWELLTIME ESCO MIN", 24) == 0)
{
*len = scnprintf(extra, n, "BTCGETDWELLTIME ESCO MIN %u\n",
(int)pCfg->min_chntime_btc_esco);
return ret;
}
else if (strncmp(command, "BTCGETDWELLTIME SCO MAX", 23) == 0)
{
*len = scnprintf(extra, n, "BTCGETDWELLTIME SCO MAX %u\n",
(int)pCfg->max_chntime_btc_sco);
return ret;
}
else if (strncmp(command, "BTCGETDWELLTIME SCO MIN", 23) == 0)
{
*len = scnprintf(extra, n, "BTCGETDWELLTIME SCO MIN %u\n",
(int)pCfg->min_chntime_btc_sco);
return ret;
}
else
{
ret = -EINVAL;
}
return ret;
}
int hdd_drv_cmd_validate(tANI_U8 *command, int len)
{
if (command[len] != ' ')
return -EINVAL;
return 0;
}
#ifdef WLAN_AP_STA_CONCURRENCY
/**
* hdd_conc_get_dwell_time() - Get concurrency dwell time parameters
* @pCfg: Pointer to HDD context
* @command: ASCII text command that is received
* @extra: Pointer to copy data sent to user
* @n: size of 'extra' buffer
* @len: length copied to 'extra' buffer
*
* Driver commands:
* wpa_cli DRIVER CONCGETDWELLTIME ACTIVE MAX
* wpa_cli DRIVER CONCGETDWELLTIME ACTIVE MIN
* wpa_cli DRIVER CONCGETDWELLTIME PASSIVE MAX
* wpa_cli DRIVER CONCGETDWELLTIME PASSIVE MIN
*
* Return: 0 for success non-zero for failure
*/
static int hdd_conc_get_dwell_time(hdd_config_t *pCfg, tANI_U8 *command,
char *extra, tANI_U8 n, tANI_U8 *len)
{
int ret = 0;
if (!pCfg || !command || !extra || !len)
{
hddLog(LOGE, FL("Argument passsed for CONCGETDWELLTIME is incorrect"));
ret = -EINVAL;
return ret;
}
if (strncmp(command, "CONCGETDWELLTIME ACTIVE MAX", 27) == 0)
{
*len = scnprintf(extra, n, "CONCGETDWELLTIME ACTIVE MAX %u\n",
(int)pCfg->nActiveMaxChnTimeConc);
return ret;
}
else if (strncmp(command, "CONCGETDWELLTIME ACTIVE MIN", 27) == 0)
{
*len = scnprintf(extra, n, "CONCGETDWELLTIME ACTIVE MIN %u\n",
(int)pCfg->nActiveMinChnTimeConc);
return ret;
}
else if (strncmp(command, "CONCGETDWELLTIME PASSIVE MAX", 28) == 0)
{
*len = scnprintf(extra, n, "CONCGETDWELLTIME PASSIVE MAX %u\n",
(int)pCfg->nPassiveMaxChnTimeConc);
return ret;
}
else if (strncmp(command, "CONCGETDWELLTIME PASSIVE MIN", 28) == 0)
{
*len = scnprintf(extra, n, "CONCGETDWELLTIME PASSIVE MIN %u\n",
(int)pCfg->nPassiveMinChnTimeConc);
return ret;
}
else
{
ret = -EINVAL;
}
return ret;
}
/**
* hdd_conc_set_dwell_time() - Set concurrency dwell time parameters
* @pAdapter: Adapter upon which the command was received
* @command: ASCII text command that is received
*
* Driver commands:
* wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MAX <value>
* wpa_cli DRIVER CONCSETDWELLTIME ACTIVE MIN <value>
* wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MAX <value
* wpa_cli DRIVER CONCSETDWELLTIME PASSIVE MIN <value>
*
* Return: 0 for success non-zero for failure
*/
static int hdd_conc_set_dwell_time(hdd_adapter_t *pAdapter, tANI_U8 *command)
{
tHalHandle hHal;
hdd_config_t *pCfg;
tANI_U8 *value = command;
int val = 0, ret = 0, temp = 0;
tSmeConfigParams smeConfig;
if (!pAdapter || !command || !(pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini)
|| !(hHal = (WLAN_HDD_GET_HAL_CTX(pAdapter))))
{
hddLog(LOGE, FL("Argument passed for CONCSETDWELLTIME is incorrect"));
ret = -EINVAL;
return ret;
}
vos_mem_zero(&smeConfig, sizeof(smeConfig));
sme_GetConfigParam(hHal, &smeConfig);
if (strncmp(command, "CONCSETDWELLTIME ACTIVE MAX", 27) == 0 )
{
if (hdd_drv_cmd_validate(command, 27)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 28;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_CONC_MIN ||
val > CFG_ACTIVE_MAX_CHANNEL_TIME_CONC_MAX)
{
hddLog(LOGE, FL("Argument passed for CONCSETDWELLTIME ACTIVE MAX is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->nActiveMaxChnTimeConc = val;
smeConfig.csrConfig.nActiveMaxChnTimeConc = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "CONCSETDWELLTIME ACTIVE MIN", 27) == 0)
{
if (hdd_drv_cmd_validate(command, 27)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 28;
temp = kstrtou32(value, 10, &val);
if (temp !=0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_CONC_MIN ||
val > CFG_ACTIVE_MIN_CHANNEL_TIME_CONC_MAX)
{
hddLog(LOGE, FL("Argument passsed for CONCSETDWELLTIME ACTIVE MIN is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->nActiveMinChnTimeConc = val;
smeConfig.csrConfig.nActiveMinChnTimeConc = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "CONCSETDWELLTIME PASSIVE MAX", 28) == 0)
{
if (hdd_drv_cmd_validate(command, 28)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 29;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_CONC_MIN ||
val > CFG_PASSIVE_MAX_CHANNEL_TIME_CONC_MAX)
{
hddLog(LOGE, FL("Argument passed for CONCSETDWELLTIME PASSIVE MAX is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->nPassiveMaxChnTimeConc = val;
smeConfig.csrConfig.nPassiveMaxChnTimeConc = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "CONCSETDWELLTIME PASSIVE MIN", 28) == 0)
{
if (hdd_drv_cmd_validate(command, 28)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 29;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_CONC_MIN ||
val > CFG_PASSIVE_MIN_CHANNEL_TIME_CONC_MAX )
{
hddLog(LOGE, FL("Argument passed for CONCSETDWELLTIME PASSIVE MIN is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->nPassiveMinChnTimeConc = val;
smeConfig.csrConfig.nPassiveMinChnTimeConc = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else
{
ret = -EINVAL;
}
return ret;
}
#endif
/**
* hdd_btc_set_dwell_time() - Set BTC dwell time parameters
* @pAdapter: Adapter upon which the command was received
* @command: ASCII text command that is received
*
* Driver commands:
* wpa_cli DRIVER BTCSETDWELLTIME ESCO MAX <value>
* wpa_cli DRIVER BTCSETDWELLTIME ESCO MIN <value>
* wpa_cli DRIVER BTCSETDWELLTIME SCO MAX <value>
* wpa_cli DRIVER BTCSETDWELLTIME SCO MIN <value>
*
* Return: 0 for success non-zero for failure
*/
static int hdd_btc_set_dwell_time(hdd_adapter_t *pAdapter, tANI_U8 *command)
{
tHalHandle hHal;
hdd_config_t *pCfg;
tANI_U8 *value = command;
int val = 0, ret = 0, temp = 0;
tSmeConfigParams smeConfig;
if (!pAdapter || !command || !(pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini)
|| !(hHal = (WLAN_HDD_GET_HAL_CTX(pAdapter))))
{
hddLog(LOGE, FL("Argument passed for BTCSETDWELLTIME is incorrect"));
ret = -EINVAL;
return ret;
}
vos_mem_zero(&smeConfig, sizeof(smeConfig));
sme_GetConfigParam(hHal, &smeConfig);
if (strncmp(command, "BTCSETDWELLTIME ESCO MAX", 24) == 0)
{
if (hdd_drv_cmd_validate(command, 24)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 25;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MIN ||
val > CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MAX)
{
hddLog(LOGE, FL("Argument passed for BTCSETDWELLTIME ESCO MAX is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->max_chntime_btc_esco = val;
smeConfig.csrConfig.max_chntime_btc_esco = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "BTCSETDWELLTIME ESCO MIN", 24) == 0)
{
if (hdd_drv_cmd_validate(command, 24)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 25;
temp = kstrtou32(value, 10, &val);
if (temp !=0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MIN ||
val > CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MAX)
{
hddLog(LOGE, FL("Argument passsed for BTCSETDWELLTIME ESCO MIN is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->min_chntime_btc_esco = val;
smeConfig.csrConfig.min_chntime_btc_esco = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "BTCSETDWELLTIME SCO MAX", 23) == 0)
{
if (hdd_drv_cmd_validate(command, 23)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 24;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_SCO_MIN ||
val > CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_SCO_MAX)
{
hddLog(LOGE, FL("Argument passed for BTCSETDWELLTIME SCO MAX is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->max_chntime_btc_sco = val;
smeConfig.csrConfig.max_chntime_btc_sco = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "BTCSETDWELLTIME SCO MIN", 23) == 0)
{
if (hdd_drv_cmd_validate(command, 23)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
value = value + 24;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_SCO_MIN ||
val > CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_SCO_MAX)
{
hddLog(LOGE, FL("Argument passed for BTCSETDWELLTIME SCO MIN is incorrect"));
ret = -EFAULT;
return ret;
}
pCfg->min_chntime_btc_sco = val;
smeConfig.csrConfig.min_chntime_btc_sco = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else
{
ret = -EINVAL;
}
return ret;
}
static int hdd_set_dwell_time(hdd_adapter_t *pAdapter, tANI_U8 *command)
{
tHalHandle hHal;
hdd_config_t *pCfg;
tANI_U8 *value = command;
int val = 0, ret = 0, temp = 0;
tSmeConfigParams smeConfig;
if (!pAdapter || !command || !(pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini)
|| !(hHal = (WLAN_HDD_GET_HAL_CTX(pAdapter))))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passed for SETDWELLTIME is incorrect", __func__);
ret = -EINVAL;
return ret;
}
vos_mem_zero(&smeConfig, sizeof(smeConfig));
sme_GetConfigParam(hHal, &smeConfig);
if (strncmp(command, "SETDWELLTIME ACTIVE MAX", 23) == 0 )
{
if (hdd_drv_cmd_validate(command, 23))
return -EINVAL;
value = value + 24;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passed for SETDWELLTIME ACTIVE MAX is incorrect", __func__);
ret = -EFAULT;
return ret;
}
pCfg->nActiveMaxChnTime = val;
smeConfig.csrConfig.nActiveMaxChnTime = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "SETDWELLTIME ACTIVE MIN", 23) == 0)
{
if (hdd_drv_cmd_validate(command, 23))
return -EINVAL;
value = value + 24;
temp = kstrtou32(value, 10, &val);
if (temp !=0 || val < CFG_ACTIVE_MIN_CHANNEL_TIME_MIN ||
val > CFG_ACTIVE_MIN_CHANNEL_TIME_MAX )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passsed for SETDWELLTIME ACTIVE MIN is incorrect", __func__);
ret = -EFAULT;
return ret;
}
pCfg->nActiveMinChnTime = val;
smeConfig.csrConfig.nActiveMinChnTime = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "SETDWELLTIME PASSIVE MAX", 24) == 0)
{
if (hdd_drv_cmd_validate(command, 24))
return -EINVAL;
value = value + 25;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_PASSIVE_MAX_CHANNEL_TIME_MIN ||
val > CFG_PASSIVE_MAX_CHANNEL_TIME_MAX )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passed for SETDWELLTIME PASSIVE MAX is incorrect", __func__);
ret = -EFAULT;
return ret;
}
pCfg->nPassiveMaxChnTime = val;
smeConfig.csrConfig.nPassiveMaxChnTime = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "SETDWELLTIME PASSIVE MIN", 24) == 0)
{
if (hdd_drv_cmd_validate(command, 24))
return -EINVAL;
value = value + 25;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_PASSIVE_MIN_CHANNEL_TIME_MIN ||
val > CFG_PASSIVE_MIN_CHANNEL_TIME_MAX )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passed for SETDWELLTIME PASSIVE MIN is incorrect", __func__);
ret = -EFAULT;
return ret;
}
pCfg->nPassiveMinChnTime = val;
smeConfig.csrConfig.nPassiveMinChnTime = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else if (strncmp(command, "SETDWELLTIME", 12) == 0)
{
if (hdd_drv_cmd_validate(command, 12))
return -EINVAL;
value = value + 13;
temp = kstrtou32(value, 10, &val);
if (temp != 0 || val < CFG_ACTIVE_MAX_CHANNEL_TIME_MIN ||
val > CFG_ACTIVE_MAX_CHANNEL_TIME_MAX )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: argument passed for SETDWELLTIME is incorrect", __func__);
ret = -EFAULT;
return ret;
}
pCfg->nActiveMaxChnTime = val;
smeConfig.csrConfig.nActiveMaxChnTime = val;
sme_UpdateConfig(hHal, &smeConfig);
}
else
{
ret = -EINVAL;
}
return ret;
}
static int hdd_cmd_setFccChannel(hdd_context_t *pHddCtx, tANI_U8 *cmd,
tANI_U8 cmd_len)
{
tANI_U8 *value;
tANI_U8 fcc_constraint;
eHalStatus status;
int ret = 0;
value = cmd + cmd_len + 1;
ret = kstrtou8(value, 10, &fcc_constraint);
if ((ret < 0) || (fcc_constraint > 1)) {
/*
* If the input value is greater than max value of datatype,
* then also it is a failure
*/
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: value out of range", __func__);
return -EINVAL;
}
status = sme_handleSetFccChannel(pHddCtx->hHal, fcc_constraint,
pHddCtx->scan_info.mScanPending);
if (status != eHAL_STATUS_SUCCESS)
ret = -EPERM;
return ret;
}
/**---------------------------------------------------------------------------
\brief hdd_enable_disable_ca_event() - When Host sends IOCTL (enabled),
FW will send *ONE* CA ind to Host(even though it is duplicate).
When Host send IOCTL (disable), FW doesn't perform any action.
Whenever any change in CA *and* WLAN is in SAP/P2P-GO mode, FW
sends CA ind to host. (regard less of IOCTL status)
\param - pHddCtx - HDD context
\param - command - command received from framework
\param - cmd_len - len of the command
\return - 0 on success, appropriate error values on failure.
--------------------------------------------------------------------------*/
int hdd_enable_disable_ca_event(hdd_context_t *pHddCtx, tANI_U8* command, tANI_U8 cmd_len)
{
tANI_U8 set_value;
int ret = 0;
eHalStatus status;
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
ret = -EINVAL;
goto exit;
}
if (pHddCtx->cfg_ini->gOptimizeCAevent == 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "Enable gOptimizeCAevent"
" ini param to control channel avooidance indication");
ret = 0;
goto exit;
}
set_value = command[cmd_len + 1] - '0';
status = sme_enableDisableChanAvoidIndEvent(pHddCtx->hHal, set_value);
if (status != eHAL_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to send"
" enableDisableChanAoidance command to SME\n", __func__);
ret = -EINVAL;
}
exit:
return ret;
}
/**
* wlan_hdd_fastreassoc_handoff_request() - Post Handoff request to SME
* @pHddCtx: Pointer to the HDD context
* @channel: channel to reassociate
* @targetApBssid: Target AP/BSSID to reassociate
*
* Return: None
*/
#if defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD) && !defined(QCA_WIFI_ISOC)
static void wlan_hdd_fastreassoc_handoff_request(hdd_context_t *pHddCtx,
uint8_t channel, tSirMacAddr targetApBssid)
{
tCsrHandoffRequest handoffInfo;
handoffInfo.channel = channel;
handoffInfo.src = FASTREASSOC;
vos_mem_copy(handoffInfo.bssid, targetApBssid, sizeof(tSirMacAddr));
sme_HandoffRequest(pHddCtx->hHal, &handoffInfo);
}
#else
static void wlan_hdd_fastreassoc_handoff_request(hdd_context_t *pHddCtx,
uint8_t channel, tSirMacAddr targetApBssid)
{
}
#endif
/**
* csr_fastroam_neighbor_ap_event() - Function to trigger scan/roam
* @pAdapter: Pointer to HDD adapter
* @channel: Channel to scan/roam
* @targetApBssid: BSSID to roam
*
* Return: None
*/
#ifdef QCA_WIFI_ISOC
static void csr_fastroam_neighbor_ap_event(hdd_adapter_t *pAdapter,
uint8_t channel, tSirMacAddr targetApBssid)
{
smeIssueFastRoamNeighborAPEvent(WLAN_HDD_GET_HAL_CTX(pAdapter),
&targetApBssid[0], eSME_ROAM_TRIGGER_SCAN, channel);
}
#else
static void csr_fastroam_neighbor_ap_event(hdd_adapter_t *pAdapter,
uint8_t channel, tSirMacAddr targetApBssid)
{
}
#endif
/**
* wlan_hdd_handle_fastreassoc() - Handle fastreassoc command
* @pAdapter: pointer to hdd adapter
* @command: pointer to the command received
*
* Return: VOS_STATUS enum
*/
static VOS_STATUS wlan_hdd_handle_fastreassoc(hdd_adapter_t *pAdapter,
uint8_t *command)
{
tANI_U8 *value = command;
tANI_U8 channel = 0;
tSirMacAddr targetApBssid;
hdd_station_ctx_t *pHddStaCtx = NULL;
hdd_context_t *pHddCtx = NULL;
int ret;
tCsrRoamModifyProfileFields mod_profile_fields;
uint32_t roam_id = 0;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
/* if not associated, no need to proceed with reassoc */
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
hddLog(LOG1, FL("Not associated!"));
return eHAL_STATUS_FAILURE;
}
ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid, &channel);
if (ret) {
hddLog(LOGE, FL("Failed to parse reassoc command data"));
return eHAL_STATUS_FAILURE;
}
/* if the target bssid is same as currently associated AP,
then no need to proceed with reassoc */
if (vos_mem_compare(targetApBssid,
pHddStaCtx->conn_info.bssId,
sizeof(tSirMacAddr))) {
sme_GetModifyProfileFields(pHddCtx->hHal, pAdapter->sessionId,
&mod_profile_fields);
sme_RoamReassoc(pHddCtx->hHal, pAdapter->sessionId, NULL,
mod_profile_fields, &roam_id, 1);
hddLog(LOG1, FL("Reassoc BSSID is same as currently associated AP bssid"));
return eHAL_STATUS_SUCCESS;
}
/* Check channel number is a valid channel number */
if (VOS_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(pAdapter, channel)) {
hddLog(LOGE, FL("Invalid Channel [%d]"), channel);
return eHAL_STATUS_FAILURE;
}
/* Proceed with reassoc */
wlan_hdd_fastreassoc_handoff_request(pHddCtx, channel, targetApBssid);
/* Proceed with scan/roam */
csr_fastroam_neighbor_ap_event(pAdapter, channel, targetApBssid);
return eHAL_STATUS_SUCCESS;
}
/**
* hdd_assign_reassoc_handoff - Set handoff source as REASSOC
* @handoffInfo: Pointer to the csr Handoff Request.
*
* Return: None
*/
#ifndef QCA_WIFI_ISOC
static inline void hdd_assign_reassoc_handoff(tCsrHandoffRequest *handoffInfo)
{
handoffInfo->src = REASSOC;
}
#else
static inline void hdd_assign_reassoc_handoff(tCsrHandoffRequest *handoffInfo)
{
}
#endif
/**
* wlan_hdd_free_cache_channels() - Free the cache channels list
* @hdd_ctx: Pointer to HDD context
*
* Return: None
*/
static void wlan_hdd_free_cache_channels(hdd_context_t *hdd_ctx)
{
if(!hdd_ctx || !hdd_ctx->original_channels)
return;
mutex_lock(&hdd_ctx->cache_channel_lock);
hdd_ctx->original_channels->num_channels = 0;
vos_mem_free(hdd_ctx->original_channels->channel_info);
hdd_ctx->original_channels->channel_info = NULL;
vos_mem_free(hdd_ctx->original_channels);
hdd_ctx->original_channels = NULL;
mutex_unlock(&hdd_ctx->cache_channel_lock);
}
/**
* hdd_alloc_chan_cache() - Allocate the memory to cache the channel
* info for the channels received in command SET_DISABLE_CHANNEL_LIST
* @hdd_ctx: Pointer to HDD context
* @num_chan: Number of channels for which memory needs to
* be allocated
*
* Return: 0 on success and error code on failure
*/
int hdd_alloc_chan_cache(hdd_context_t *hdd_ctx, int num_chan)
{
hdd_ctx->original_channels =
vos_mem_malloc(sizeof(struct hdd_cache_channels));
if (!hdd_ctx->original_channels) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"In %s, VOS_MALLOC_ERR", __func__);
return -EINVAL;
}
hdd_ctx->original_channels->num_channels = num_chan;
hdd_ctx->original_channels->channel_info =
vos_mem_malloc(num_chan *
sizeof(struct hdd_cache_channel_info));
if (!hdd_ctx->original_channels->channel_info) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"In %s, VOS_MALLOC_ERR", __func__);
hdd_ctx->original_channels->num_channels = 0;
vos_mem_free(hdd_ctx->original_channels);
hdd_ctx->original_channels = NULL;
return -ENOMEM;
}
return 0;
}
int hdd_parse_disable_chan_cmd(hdd_adapter_t *adapter, tANI_U8 *ptr)
{
v_PVOID_t pvosGCtx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
hdd_context_t *hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, pvosGCtx);
tANI_U8 *param;
int j, tempInt, ret = 0, i, num_channels;
int parsed_channels[MAX_CHANNEL];
bool is_command_repeated = false;
if (NULL == pvosGCtx) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"VOS Global Context is NULL");
return -EINVAL;
}
if (NULL == hdd_ctx) {
hddLog(VOS_TRACE_LEVEL_FATAL, "HDD Context is NULL");
return -EINVAL;
}
param = strchr(ptr, ' ');
/*no argument after the command*/
if (NULL == param)
return -EINVAL;
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *param)
return -EINVAL;
param++;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param))
param++;
/*no argument followed by spaces*/
if ('\0' == *param)
return -EINVAL;
/*getting the first argument ie the number of channels*/
if (sscanf(param, "%d ", &tempInt) != 1) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot get number of channels from input",
__func__);
return -EINVAL;
}
if (tempInt < 0 || tempInt > MAX_CHANNEL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid Number of channel received", __func__);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Number of channel to disable are: %d",
__func__, tempInt);
if (!tempInt) {
if (!wlan_hdd_restore_channels(hdd_ctx)) {
/*
* Free the cache channels only when the command is
* received with num channels as 0
*/
wlan_hdd_free_cache_channels(hdd_ctx);
}
return 0;
}
mutex_lock(&hdd_ctx->cache_channel_lock);
if (!hdd_ctx->original_channels) {
if (hdd_alloc_chan_cache(hdd_ctx, tempInt)) {
ret = -ENOMEM;
goto mem_alloc_failed;
}
} else if (hdd_ctx->original_channels->num_channels != tempInt) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s, Invalid No of channel provided in the list",
__func__);
ret = -EINVAL;
is_command_repeated = true;
goto parse_failed;
} else {
is_command_repeated = true;
}
num_channels = tempInt;
for (j = 0; j < num_channels; j++) {
/*
* param pointing to the beginning of first space
* after number of channels
*/
param = strpbrk(param, " ");
/*no channel list after the number of channels argument*/
if (NULL == param) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s, Invalid No of channel provided in the list",
__func__);
ret = -EINVAL;
goto parse_failed;
}
param++;
/*removing empty space*/
while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param))
param++;
if ('\0' == *param) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s, No channel is provided in the list",
__func__);
ret = -EINVAL;
goto parse_failed;
}
if (sscanf(param, "%d ", &tempInt) != 1) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot read channel number",
__func__);
ret = -EINVAL;
goto parse_failed;
}
if (!IS_CHANNEL_VALID(tempInt)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid channel number received",
__func__);
ret = -EINVAL;
goto parse_failed;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: channel[%d] = %d", __func__,
j, tempInt);
parsed_channels[j] = tempInt;
}
/*extra arguments check*/
param = strchr(param, ' ');
if (NULL != param) {
while ((SPACE_ASCII_VALUE == *param) && ('\0' != *param))
param++;
if ('\0' != *param) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid argument received", __func__);
ret = -EINVAL;
goto parse_failed;
}
}
/*
* If command is received first time, cache the channels to
* be disabled else compare the channels received in the
* command with the cached channels, if channel list matches
* return success otherewise return failure.
*/
if (!is_command_repeated)
for (j = 0; j < num_channels; j++)
hdd_ctx->original_channels->
channel_info[j].channel_num =
parsed_channels[j];
else {
for (i = 0; i < num_channels; i++) {
for (j = 0; j < num_channels; j++)
if (hdd_ctx->original_channels->
channel_info[i].channel_num ==
parsed_channels[j])
break;
if (j == num_channels) {
ret = -EINVAL;
goto parse_failed;
}
}
ret = 0;
}
mem_alloc_failed:
mutex_unlock(&hdd_ctx->cache_channel_lock);
/* Disable the channels received in command SET_DISABLE_CHANNEL_LIST*/
if (!is_command_repeated) {
wlan_hdd_disable_channels(hdd_ctx);
hdd_check_and_disconnect_sta_on_invalid_channel(hdd_ctx);
}
EXIT();
return ret;
parse_failed:
mutex_unlock(&hdd_ctx->cache_channel_lock);
if (!is_command_repeated)
wlan_hdd_free_cache_channels(hdd_ctx);
EXIT();
return ret;
}
int hdd_get_disable_ch_list(hdd_context_t *hdd_ctx, tANI_U8 *buf,
uint32_t buf_len)
{
struct hdd_cache_channel_info *ch_list;
unsigned char i, num_ch;
int len = 0;
mutex_lock(&hdd_ctx->cache_channel_lock);
if (hdd_ctx->original_channels &&
hdd_ctx->original_channels->num_channels &&
hdd_ctx->original_channels->channel_info) {
num_ch = hdd_ctx->original_channels->num_channels;
len = scnprintf(buf, buf_len, "%s %hhu",
"GET_DISABLE_CHANNEL_LIST", num_ch);
ch_list = hdd_ctx->original_channels->channel_info;
for (i = 0; (i < num_ch) && (len < buf_len-1); i++) {
len += scnprintf(buf + len, buf_len - len,
" %d", ch_list[i].channel_num);
}
}
mutex_unlock(&hdd_ctx->cache_channel_lock);
return len;
}
static int hdd_driver_command(hdd_adapter_t *pAdapter,
hdd_priv_data_t *ppriv_data)
{
hdd_priv_data_t priv_data;
tANI_U8 *command = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_scaninfo_t *pScanInfo = NULL;
int ret = 0;
int status;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
struct cfg80211_mgmt_tx_params params;
#endif
ENTER();
/*
* Note that valid pointers are provided by caller
*/
/* copy to local struct to avoid numerous changes to legacy code */
priv_data = *ppriv_data;
if (priv_data.total_len <= 0 ||
priv_data.total_len > WLAN_PRIV_DATA_MAX_LEN)
{
hddLog(VOS_TRACE_LEVEL_WARN,
"%s:invalid priv_data.total_len(%d)!!!", __func__,
priv_data.total_len);
ret = -EINVAL;
goto exit;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
ret = -EINVAL;
goto exit;
}
/* Allocate +1 for '\0' */
command = kmalloc(priv_data.total_len + 1, GFP_KERNEL);
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';
/* at one time the following block of code was conditional. braces
* have been retained to avoid re-indenting the legacy code
*/
{
hdd_context_t *pHddCtx = (hdd_context_t*)pAdapter->pHddCtx;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received %s cmd from Wi-Fi GUI***", __func__, command);
if (strncmp(command, "P2P_DEV_ADDR", 12) == 0 )
{
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_P2P_DEV_ADDR_IOCTL,
pAdapter->sessionId, (unsigned)
(*(pHddCtx->p2pDeviceAddress.bytes+2)<<24 |
*(pHddCtx->p2pDeviceAddress.bytes+3)<<16 |
*(pHddCtx->p2pDeviceAddress.bytes+4)<<8 |
*(pHddCtx->p2pDeviceAddress.bytes+5))));
if (copy_to_user(priv_data.buf, pHddCtx->p2pDeviceAddress.bytes,
sizeof(tSirMacAddr)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
}
}
else if(strncmp(command, "SETBAND", 7) == 0)
{
tANI_U8 *ptr = command ;
ret = hdd_drv_cmd_validate(command, 7);
if (ret)
goto exit;
/* Change band request received */
/* First 8 bytes will have "SETBAND " and
* 9 byte will have band setting value */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: SetBandCommand Info comm %s UL %d, TL %d", __func__, command, priv_data.used_len, priv_data.total_len);
if(VOS_FTM_MODE != hdd_get_conparam())
{
/* Change band request received */
ret = hdd_setBand_helper(pAdapter->dev, ptr);
if(ret < 0)
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: failed to set band ret=%d", __func__, ret);
}
}
else if(strncmp(command, "SETWMMPS", 8) == 0)
{
tANI_U8 *ptr = command;
ret = hdd_drv_cmd_validate(command, 8);
if (ret)
goto exit;
ret = hdd_wmmps_helper(pAdapter, ptr);
}
else if(strncmp(command, "TDLSSCAN", 8) == 0)
{
tANI_U8 *ptr = command;
ret = hdd_drv_cmd_validate(command, 8);
if (ret)
goto exit;
ret = hdd_set_tdls_scan_type(pAdapter, ptr);
}
else if ( strncasecmp(command, "COUNTRY", 7) == 0 )
{
char *country_code;
ret = hdd_drv_cmd_validate(command, 7);
if (ret)
goto exit;
country_code = command + 8;
INIT_COMPLETION(pAdapter->change_country_code);
hdd_checkandupdate_dfssetting(pAdapter, country_code);
#ifndef CONFIG_ENABLE_LINUX_REG
hdd_checkandupdate_phymode(pAdapter, country_code);
#endif
ret = (int)sme_ChangeCountryCode(pHddCtx->hHal,
(void *)(tSmeChangeCountryCallback)
wlan_hdd_change_country_code_callback,
country_code, pAdapter, pHddCtx->pvosContext, eSIR_TRUE, eSIR_TRUE);
if (eHAL_STATUS_SUCCESS == ret)
{
ret = wait_for_completion_interruptible_timeout(
&pAdapter->change_country_code,
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (0 >= ret)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: SME while setting country code timed out %d",
__func__, ret);
}
}
else
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: SME Change Country code fail ret=%d", __func__, ret);
ret = -EINVAL;
}
}
/*
command should be a string having format
SET_SAP_CHANNEL_LIST <num of channels> <the channels seperated by spaces>
*/
else if(strncmp(command, "SET_SAP_CHANNEL_LIST", 20) == 0)
{
tANI_U8 *ptr = command;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" Received Command to Set Preferred Channels for SAP in %s", __func__);
ret = sapSetPreferredChannel(ptr);
}
else if (strncmp(command, "VOWIFIMODE", 10) == 0)
{
tANI_U8 *ptr;
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Received Command for VOWIFI mode in %s", __func__);
ptr = (tANI_U8*)command + 11;
hdd_set_vowifi_mode(pHddCtx, *ptr - '0');
}
else if(strncmp(command, "SETSUSPENDMODE", 14) == 0)
{
int suspend = 0;
tANI_U8 *ptr;
ret = hdd_drv_cmd_validate(command, 14);
if (ret)
goto exit;
ptr = (tANI_U8*)command + 15;
suspend = *ptr - '0';
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETSUSPENDMODE_IOCTL,
pAdapter->sessionId, suspend));
hdd_set_wlan_suspend_mode(suspend);
}
#ifdef WLAN_FEATURE_NEIGHBOR_ROAMING
else if (strncmp(command, "SETROAMTRIGGER", 14) == 0)
{
tANI_U8 *value = command;
tANI_S8 rssi = 0;
tANI_U8 lookUpThreshold = CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_DEFAULT;
eHalStatus status = eHAL_STATUS_SUCCESS;
ret = hdd_drv_cmd_validate(command, 14);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMTRIGGER<delimiter> */
value = value + 15;
/* Convert the value from ascii to integer */
ret = kstrtos8(value, 10, &rssi);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed Input value may be out of range[%d - %d]",
__func__,
CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
ret = -EINVAL;
goto exit;
}
lookUpThreshold = abs(rssi);
if ((lookUpThreshold < CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN) ||
(lookUpThreshold > CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Neighbor lookup threshold value %d is out of range"
" (Min: %d Max: %d)", lookUpThreshold,
CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MIN,
CFG_NEIGHBOR_LOOKUP_RSSI_THRESHOLD_MAX);
ret = -EINVAL;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMTRIGGER_IOCTL,
pAdapter->sessionId, lookUpThreshold));
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set Roam trigger"
" (Neighbor lookup threshold) = %d", __func__, lookUpThreshold);
pHddCtx->cfg_ini->nNeighborLookupRssiThreshold = lookUpThreshold;
status = sme_setNeighborLookupRssiThreshold((tHalHandle)(pHddCtx->hHal), lookUpThreshold);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to set roam trigger, try again", __func__);
ret = -EPERM;
goto exit;
}
/* Set Reassoc threshold to (lookup rssi threshold + 5 dBm) */
pHddCtx->cfg_ini->nNeighborReassocRssiThreshold = lookUpThreshold + 5;
sme_setNeighborReassocRssiThreshold((tHalHandle)(pHddCtx->hHal), lookUpThreshold + 5);
}
else if (strncmp(command, "GETROAMTRIGGER", 14) == 0)
{
tANI_U8 lookUpThreshold = sme_getNeighborLookupRssiThreshold((tHalHandle)(pHddCtx->hHal));
int rssi = (-1) * lookUpThreshold;
char extra[32];
tANI_U8 len = 0;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETROAMTRIGGER_IOCTL,
pAdapter->sessionId, lookUpThreshold));
len = scnprintf(extra, sizeof(extra), "%s %d", command, rssi);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETROAMSCANPERIOD", 17) == 0)
{
tANI_U8 *value = command;
tANI_U8 roamScanPeriod = 0;
tANI_U16 neighborEmptyScanRefreshPeriod = CFG_EMPTY_SCAN_REFRESH_PERIOD_DEFAULT;
ret = hdd_drv_cmd_validate(command, 17);
if (ret)
goto exit;
/* input refresh period is in terms of seconds */
/* Move pointer to ahead of SETROAMSCANPERIOD<delimiter> */
value = value + 18;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &roamScanPeriod);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed Input value may be out of range[%d - %d]",
__func__,
(CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000),
(CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000));
ret = -EINVAL;
goto exit;
}
if ((roamScanPeriod < (CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000)) ||
(roamScanPeriod > (CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Roam scan period value %d is out of range"
" (Min: %d Max: %d)", roamScanPeriod,
(CFG_EMPTY_SCAN_REFRESH_PERIOD_MIN/1000),
(CFG_EMPTY_SCAN_REFRESH_PERIOD_MAX/1000));
ret = -EINVAL;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMSCANPERIOD_IOCTL,
pAdapter->sessionId, roamScanPeriod));
neighborEmptyScanRefreshPeriod = roamScanPeriod * 1000;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set roam scan period"
" (Empty Scan refresh period) = %d", __func__, roamScanPeriod);
pHddCtx->cfg_ini->nEmptyScanRefreshPeriod = neighborEmptyScanRefreshPeriod;
sme_UpdateEmptyScanRefreshPeriod((tHalHandle)(pHddCtx->hHal), neighborEmptyScanRefreshPeriod);
}
else if (strncmp(command, "GETROAMSCANPERIOD", 17) == 0)
{
tANI_U16 nEmptyScanRefreshPeriod = sme_getEmptyScanRefreshPeriod((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETROAMSCANPERIOD_IOCTL,
pAdapter->sessionId, nEmptyScanRefreshPeriod));
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETROAMSCANPERIOD", (nEmptyScanRefreshPeriod/1000));
/* Returned value is in units of seconds */
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETROAMSCANREFRESHPERIOD", 24) == 0)
{
tANI_U8 *value = command;
tANI_U8 roamScanRefreshPeriod = 0;
tANI_U16 neighborScanRefreshPeriod = CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_DEFAULT;
ret = hdd_drv_cmd_validate(command, 24);
if (ret)
goto exit;
/* input refresh period is in terms of seconds */
/* Move pointer to ahead of SETROAMSCANREFRESHPERIOD<delimiter> */
value = value + 25;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &roamScanRefreshPeriod);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed Input value may be out of range[%d - %d]",
__func__,
(CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000),
(CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000));
ret = -EINVAL;
goto exit;
}
if ((roamScanRefreshPeriod < (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000)) ||
(roamScanRefreshPeriod > (CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Neighbor scan results refresh period value %d is out of range"
" (Min: %d Max: %d)", roamScanRefreshPeriod,
(CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MIN/1000),
(CFG_NEIGHBOR_SCAN_RESULTS_REFRESH_PERIOD_MAX/1000));
ret = -EINVAL;
goto exit;
}
neighborScanRefreshPeriod = roamScanRefreshPeriod * 1000;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set roam scan refresh period"
" (Scan refresh period) = %d", __func__, roamScanRefreshPeriod);
pHddCtx->cfg_ini->nNeighborResultsRefreshPeriod = neighborScanRefreshPeriod;
sme_setNeighborScanRefreshPeriod((tHalHandle)(pHddCtx->hHal), neighborScanRefreshPeriod);
}
else if (strncmp(command, "GETROAMSCANREFRESHPERIOD", 24) == 0)
{
tANI_U16 value = sme_getNeighborScanRefreshPeriod((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETROAMSCANREFRESHPERIOD", (value/1000));
/* Returned value is in units of seconds */
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
#ifdef FEATURE_WLAN_LFR
/* SETROAMMODE */
else if (strncmp(command, "SETROAMMODE", SIZE_OF_SETROAMMODE) == 0)
{
tANI_U8 *value = command;
tANI_BOOLEAN roamMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
if (pHddCtx->concurrency_mode == VOS_STA_MON) {
hddLog(LOGE,
FL("Roaming is always disabled in STA + MON concurrency"));
ret = -EINVAL;
goto exit;
}
ret = hdd_drv_cmd_validate(command, SIZE_OF_SETROAMMODE);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMMODE<delimiter> */
value = value + SIZE_OF_SETROAMMODE + 1;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, SIZE_OF_SETROAMMODE, &roamMode);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_LFR_FEATURE_ENABLED_MIN,
CFG_LFR_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
if ((roamMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
(roamMode > CFG_LFR_FEATURE_ENABLED_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Roam Mode value %d is out of range"
" (Min: %d Max: %d)", roamMode,
CFG_LFR_FEATURE_ENABLED_MIN,
CFG_LFR_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set Roam Mode = %d", __func__, roamMode);
/*
* Note that
* SETROAMMODE 0 is to enable LFR while
* SETROAMMODE 1 is to disable LFR, but
* NotifyIsFastRoamIniFeatureEnabled 0/1 is to enable/disable.
* So, we have to invert the value to call sme_UpdateIsFastRoamIniFeatureEnabled.
*/
if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
roamMode = CFG_LFR_FEATURE_ENABLED_MAX; /* Roam enable */
else
roamMode = CFG_LFR_FEATURE_ENABLED_MIN; /* Roam disable */
pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled = roamMode;
sme_UpdateIsFastRoamIniFeatureEnabled((tHalHandle)(pHddCtx->hHal), roamMode);
}
/* GETROAMMODE */
else if (strncmp(command, "GETROAMMODE", SIZE_OF_GETROAMMODE) == 0)
{
tANI_BOOLEAN roamMode = sme_getIsLfrFeatureEnabled((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/*
* roamMode value shall be inverted because the sementics is different.
*/
if (CFG_LFR_FEATURE_ENABLED_MIN == roamMode)
roamMode = CFG_LFR_FEATURE_ENABLED_MAX;
else
roamMode = CFG_LFR_FEATURE_ENABLED_MIN;
len = scnprintf(extra, sizeof(extra), "%s %d", command, roamMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
#endif
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
else if (strncmp(command, "SETROAMDELTA", 12) == 0)
{
tANI_U8 *value = command;
tANI_U8 roamRssiDiff = CFG_ROAM_RSSI_DIFF_DEFAULT;
ret = hdd_drv_cmd_validate(command, 12);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMDELTA<delimiter> */
value = value + 13;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &roamRssiDiff);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ROAM_RSSI_DIFF_MIN,
CFG_ROAM_RSSI_DIFF_MAX);
ret = -EINVAL;
goto exit;
}
if ((roamRssiDiff < CFG_ROAM_RSSI_DIFF_MIN) ||
(roamRssiDiff > CFG_ROAM_RSSI_DIFF_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Roam rssi diff value %d is out of range"
" (Min: %d Max: %d)", roamRssiDiff,
CFG_ROAM_RSSI_DIFF_MIN,
CFG_ROAM_RSSI_DIFF_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set roam rssi diff = %d", __func__, roamRssiDiff);
pHddCtx->cfg_ini->RoamRssiDiff = roamRssiDiff;
sme_UpdateRoamRssiDiff((tHalHandle)(pHddCtx->hHal), roamRssiDiff);
}
else if (strncmp(command, "GETROAMDELTA", 12) == 0)
{
tANI_U8 roamRssiDiff = sme_getRoamRssiDiff((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETROAMDELTA_IOCTL,
pAdapter->sessionId, roamRssiDiff));
len = scnprintf(extra, sizeof(extra), "%s %d",
command, roamRssiDiff);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
#endif
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
else if (strncmp(command, "GETBAND", 7) == 0)
{
int band = -1;
char extra[32];
tANI_U8 len = 0;
hdd_getBand_helper(pHddCtx, &band);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETBAND_IOCTL,
pAdapter->sessionId, band));
len = scnprintf(extra, sizeof(extra), "%s %d", command, band);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETROAMSCANCHANNELS", 19) == 0)
{
tANI_U8 *value = command;
tANI_U8 ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U8 numChannels = 0;
eHalStatus status = eHAL_STATUS_SUCCESS;
status = hdd_parse_channellist(value, ChannelList, &numChannels);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse channel list information", __func__);
ret = -EINVAL;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
pAdapter->sessionId, numChannels));
if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: number of channels (%d) supported exceeded max (%d)", __func__,
numChannels, WNI_CFG_VALID_CHANNEL_LIST_LEN);
ret = -EINVAL;
goto exit;
}
status = sme_ChangeRoamScanChannelList((tHalHandle)(pHddCtx->hHal), ChannelList,
numChannels);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to update channel list information", __func__);
ret = -EINVAL;
goto exit;
}
}
else if (strncmp(command, "GETROAMSCANCHANNELS", 19) == 0)
{
tANI_U8 ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U8 numChannels = 0;
tANI_U8 j = 0;
char extra[128] = {0};
int len;
if (eHAL_STATUS_SUCCESS != sme_getRoamScanChannelList( (tHalHandle)(pHddCtx->hHal),
ChannelList, &numChannels ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: failed to get roam scan channel list", __func__);
ret = -EFAULT;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETROAMSCANCHANNELS_IOCTL,
pAdapter->sessionId, numChannels));
/* output channel list is of the format
[Number of roam scan channels][Channel1][Channel2]... */
/* copy the number of channels in the 0th index */
len = scnprintf(extra, sizeof(extra), "%s %d", command, numChannels);
for (j = 0; (j < numChannels) && len <= sizeof(extra); j++)
{
len += scnprintf(extra + len, sizeof(extra) - len, " %d",
ChannelList[j]);
}
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "GETCCXMODE", 10) == 0)
{
tANI_BOOLEAN eseMode = sme_getIsEseFeatureEnabled((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (eseMode &&
hdd_is_okc_mode_enabled(pHddCtx) &&
sme_getIsFtFeatureEnabled((tHalHandle)(pHddCtx->hHal)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: OKC/ESE/11R are supported simultaneously"
" hence this operation is not permitted!", __func__);
ret = -EPERM;
goto exit;
}
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETCCXMODE", eseMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "GETOKCMODE", 10) == 0)
{
tANI_BOOLEAN okcMode = hdd_is_okc_mode_enabled(pHddCtx);
char extra[32];
tANI_U8 len = 0;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (okcMode &&
sme_getIsEseFeatureEnabled((tHalHandle)(pHddCtx->hHal)) &&
sme_getIsFtFeatureEnabled((tHalHandle)(pHddCtx->hHal)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: OKC/ESE/11R are supported simultaneously"
" hence this operation is not permitted!", __func__);
ret = -EPERM;
goto exit;
}
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETOKCMODE", okcMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "GETFASTROAM", 11) == 0)
{
tANI_BOOLEAN lfrMode = sme_getIsLfrFeatureEnabled((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETFASTROAM", lfrMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "GETFASTTRANSITION", 17) == 0)
{
tANI_BOOLEAN ft = sme_getIsFtFeatureEnabled((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETFASTTRANSITION", ft);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETROAMSCANCHANNELMINTIME", 25) == 0)
{
tANI_U8 *value = command;
tANI_U8 minTime = CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_DEFAULT;
ret = hdd_drv_cmd_validate(command, 25);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMSCANCHANNELMINTIME<delimiter> */
value = value + 26;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &minTime);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
ret = -EINVAL;
goto exit;
}
if ((minTime < CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN) ||
(minTime > CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"scan min channel time value %d is out of range"
" (Min: %d Max: %d)", minTime,
CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MIN,
CFG_NEIGHBOR_SCAN_MIN_CHAN_TIME_MAX);
ret = -EINVAL;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMSCANCHANNELMINTIME_IOCTL,
pAdapter->sessionId, minTime));
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change channel min time = %d", __func__, minTime);
pHddCtx->cfg_ini->nNeighborScanMinChanTime = minTime;
sme_setNeighborScanMinChanTime((tHalHandle)(pHddCtx->hHal), minTime);
}
else if (strncmp(command, "SENDACTIONFRAME", 15) == 0)
{
tANI_U8 *value = command;
tANI_U8 channel = 0;
tANI_U8 dwellTime = 0;
tANI_U8 bufLen = 0;
tANI_U8 *buf = NULL;
tSirMacAddr targetApBssid;
eHalStatus status = eHAL_STATUS_SUCCESS;
struct ieee80211_channel chan;
tANI_U8 finalLen = 0;
tANI_U8 *finalBuf = NULL;
tANI_U8 temp = 0;
u64 cookie;
hdd_station_ctx_t *pHddStaCtx = NULL;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* if not associated, no need to send action frame */
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s:Not associated!",__func__);
ret = -EINVAL;
goto exit;
}
status = hdd_parse_send_action_frame_data(value, targetApBssid, &channel,
&dwellTime, &buf, &bufLen);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse send action frame data", __func__);
ret = -EINVAL;
goto exit;
}
/* if the target bssid is different from currently associated AP,
then no need to send action frame */
if (VOS_TRUE != vos_mem_compare(targetApBssid,
pHddStaCtx->conn_info.bssId, sizeof(tSirMacAddr)))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s:STA is not associated to this AP!",__func__);
ret = -EINVAL;
vos_mem_free(buf);
buf = NULL;
goto exit;
}
/* if the channel number is different from operating channel then
no need to send action frame */
if (channel != pHddStaCtx->conn_info.operationChannel)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: channel(%d) is different from operating channel(%d)",
__func__, channel, pHddStaCtx->conn_info.operationChannel);
ret = -EINVAL;
vos_mem_free(buf);
buf = NULL;
goto exit;
}
chan.center_freq = sme_ChnToFreq(channel);
finalLen = bufLen + 24;
finalBuf = vos_mem_malloc(finalLen);
if (NULL == finalBuf)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s:memory allocation failed",__func__);
ret = -ENOMEM;
vos_mem_free(buf);
buf = NULL;
goto exit;
}
vos_mem_zero(finalBuf, finalLen);
/* Fill subtype */
temp = SIR_MAC_MGMT_ACTION << 4;
vos_mem_copy(finalBuf + 0, &temp, sizeof(temp));
/* Fill type */
temp = SIR_MAC_MGMT_FRAME;
vos_mem_copy(finalBuf + 2, &temp, sizeof(temp));
/* Fill destination address (bssid of the AP) */
vos_mem_copy(finalBuf + 4, targetApBssid, sizeof(targetApBssid));
/* Fill source address (STA mac address) */
vos_mem_copy(finalBuf + 10, pAdapter->macAddressCurrent.bytes, sizeof(pAdapter->macAddressCurrent.bytes));
/* Fill BSSID (AP mac address) */
vos_mem_copy(finalBuf + 16, targetApBssid, sizeof(targetApBssid));
/* Fill received buffer from 24th address */
vos_mem_copy(finalBuf + 24, buf, bufLen);
/* done with the parsed buffer */
vos_mem_free(buf);
buf = NULL;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
params.chan = &chan;
params.offchan = 0;
params.wait = dwellTime;
params.buf = finalBuf;
params.len = finalLen;
params.no_cck = 1;
params.dont_wait_for_ack = 1;
ret = wlan_hdd_mgmt_tx(NULL, &pAdapter->wdev, &params, &cookie);
#else
wlan_hdd_mgmt_tx( NULL,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
&(pAdapter->wdev),
#else
pAdapter->dev,
#endif
&chan, 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
NL80211_CHAN_HT20, 1,
#endif
dwellTime, finalBuf, finalLen, 1,
1, &cookie );
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)*/
vos_mem_free(finalBuf);
}
else if (strncmp(command, "GETROAMSCANCHANNELMINTIME", 25) == 0)
{
tANI_U16 val = sme_getNeighborScanMinChanTime((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/* value is interms of msec */
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETROAMSCANCHANNELMINTIME", val);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_GETROAMSCANCHANNELMINTIME_IOCTL,
pAdapter->sessionId, val));
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETSCANCHANNELTIME", 18) == 0)
{
tANI_U8 *value = command;
tANI_U16 maxTime = CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_DEFAULT;
ret = hdd_drv_cmd_validate(command, 18);
if (ret)
goto exit;
/* Move pointer to ahead of SETSCANCHANNELTIME<delimiter> */
value = value + 19;
/* Convert the value from ascii to integer */
ret = kstrtou16(value, 10, &maxTime);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou16 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou16 failed range [%d - %d]", __func__,
CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
ret = -EINVAL;
goto exit;
}
if ((maxTime < CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN) ||
(maxTime > CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"lfr mode value %d is out of range"
" (Min: %d Max: %d)", maxTime,
CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MIN,
CFG_NEIGHBOR_SCAN_MAX_CHAN_TIME_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change channel max time = %d", __func__, maxTime);
pHddCtx->cfg_ini->nNeighborScanMaxChanTime = maxTime;
sme_setNeighborScanMaxChanTime((tHalHandle)(pHddCtx->hHal), maxTime);
}
else if (strncmp(command, "GETSCANCHANNELTIME", 18) == 0)
{
tANI_U16 val = sme_getNeighborScanMaxChanTime((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/* value is interms of msec */
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETSCANCHANNELTIME", val);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETSCANHOMETIME", 15) == 0)
{
tANI_U8 *value = command;
tANI_U16 val = CFG_NEIGHBOR_SCAN_TIMER_PERIOD_DEFAULT;
ret = hdd_drv_cmd_validate(command, 15);
if (ret)
goto exit;
/* Move pointer to ahead of SETSCANHOMETIME<delimiter> */
value = value + 16;
/* Convert the value from ascii to integer */
ret = kstrtou16(value, 10, &val);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou16 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou16 failed range [%d - %d]", __func__,
CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
ret = -EINVAL;
goto exit;
}
if ((val < CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN) ||
(val > CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"scan home time value %d is out of range"
" (Min: %d Max: %d)", val,
CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MIN,
CFG_NEIGHBOR_SCAN_TIMER_PERIOD_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change scan home time = %d", __func__, val);
pHddCtx->cfg_ini->nNeighborScanPeriod = val;
sme_setNeighborScanPeriod((tHalHandle)(pHddCtx->hHal), val);
}
else if (strncmp(command, "GETSCANHOMETIME", 15) == 0)
{
tANI_U16 val = sme_getNeighborScanPeriod((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/* value is interms of msec */
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETSCANHOMETIME", val);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETROAMINTRABAND", 16) == 0)
{
tANI_U8 *value = command;
tANI_U8 val = CFG_ROAM_INTRA_BAND_DEFAULT;
ret = hdd_drv_cmd_validate(command, 16);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMINTRABAND<delimiter> */
value = value + 17;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &val);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ROAM_INTRA_BAND_MIN,
CFG_ROAM_INTRA_BAND_MAX);
ret = -EINVAL;
goto exit;
}
if ((val < CFG_ROAM_INTRA_BAND_MIN) ||
(val > CFG_ROAM_INTRA_BAND_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"intra band mode value %d is out of range"
" (Min: %d Max: %d)", val,
CFG_ROAM_INTRA_BAND_MIN,
CFG_ROAM_INTRA_BAND_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change intra band = %d", __func__, val);
pHddCtx->cfg_ini->nRoamIntraBand = val;
sme_setRoamIntraBand((tHalHandle)(pHddCtx->hHal), val);
}
else if (strncmp(command, "GETROAMINTRABAND", 16) == 0)
{
tANI_U16 val = sme_getRoamIntraBand((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
/* value is interms of msec */
len = scnprintf(extra, sizeof(extra), "%s %d",
"GETROAMINTRABAND", val);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETSCANNPROBES", 14) == 0)
{
tANI_U8 *value = command;
tANI_U8 nProbes = CFG_ROAM_SCAN_N_PROBES_DEFAULT;
ret = hdd_drv_cmd_validate(command, 14);
if (ret)
goto exit;
/* Move pointer to ahead of SETSCANNPROBES<delimiter> */
value = value + 15;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &nProbes);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ROAM_SCAN_N_PROBES_MIN,
CFG_ROAM_SCAN_N_PROBES_MAX);
ret = -EINVAL;
goto exit;
}
if ((nProbes < CFG_ROAM_SCAN_N_PROBES_MIN) ||
(nProbes > CFG_ROAM_SCAN_N_PROBES_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"NProbes value %d is out of range"
" (Min: %d Max: %d)", nProbes,
CFG_ROAM_SCAN_N_PROBES_MIN,
CFG_ROAM_SCAN_N_PROBES_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set nProbes = %d", __func__, nProbes);
pHddCtx->cfg_ini->nProbes = nProbes;
sme_UpdateRoamScanNProbes((tHalHandle)(pHddCtx->hHal), nProbes);
}
else if (strncmp(command, "GETSCANNPROBES", 14) == 0)
{
tANI_U8 val = sme_getRoamScanNProbes((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETSCANHOMEAWAYTIME", 19) == 0)
{
tANI_U8 *value = command;
tANI_U16 homeAwayTime = CFG_ROAM_SCAN_HOME_AWAY_TIME_DEFAULT;
ret = hdd_drv_cmd_validate(command, 19);
if (ret)
goto exit;
/* Move pointer to ahead of SETSCANHOMEAWAYTIME<delimiter> */
/* input value is in units of msec */
value = value + 20;
/* Convert the value from ascii to integer */
ret = kstrtou16(value, 10, &homeAwayTime);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
ret = -EINVAL;
goto exit;
}
if ((homeAwayTime < CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN) ||
(homeAwayTime > CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"homeAwayTime value %d is out of range"
" (Min: %d Max: %d)", homeAwayTime,
CFG_ROAM_SCAN_HOME_AWAY_TIME_MIN,
CFG_ROAM_SCAN_HOME_AWAY_TIME_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set scan away time = %d", __func__, homeAwayTime);
if (pHddCtx->cfg_ini->nRoamScanHomeAwayTime != homeAwayTime)
{
pHddCtx->cfg_ini->nRoamScanHomeAwayTime = homeAwayTime;
sme_UpdateRoamScanHomeAwayTime((tHalHandle)(pHddCtx->hHal), homeAwayTime, eANI_BOOLEAN_TRUE);
}
}
else if (strncmp(command, "GETSCANHOMEAWAYTIME", 19) == 0)
{
tANI_U16 val = sme_getRoamScanHomeAwayTime((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d", command, val);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "REASSOC", 7) == 0)
{
ret = hdd_drv_cmd_validate(command, 7);
if (ret)
goto exit;
ret = hdd_parse_reassoc(pAdapter, command, priv_data.total_len);
if (!ret)
goto exit;
}
else if (strncmp(command, "SETWESMODE", 10) == 0)
{
tANI_U8 *value = command;
tANI_BOOLEAN wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
/* Move pointer to ahead of SETWESMODE<delimiter> */
value = value + 11;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &wesMode);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ENABLE_WES_MODE_NAME_MIN,
CFG_ENABLE_WES_MODE_NAME_MAX);
ret = -EINVAL;
goto exit;
}
if ((wesMode < CFG_ENABLE_WES_MODE_NAME_MIN) ||
(wesMode > CFG_ENABLE_WES_MODE_NAME_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"WES Mode value %d is out of range"
" (Min: %d Max: %d)", wesMode,
CFG_ENABLE_WES_MODE_NAME_MIN,
CFG_ENABLE_WES_MODE_NAME_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set WES Mode rssi diff = %d", __func__, wesMode);
pHddCtx->cfg_ini->isWESModeEnabled = wesMode;
sme_UpdateWESMode((tHalHandle)(pHddCtx->hHal), wesMode);
}
else if (strncmp(command, "GETWESMODE", 10) == 0)
{
tANI_BOOLEAN wesMode = sme_GetWESMode((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d", command, wesMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */
#ifdef FEATURE_WLAN_LFR
else if (strncmp(command, "SETFASTROAM", 11) == 0)
{
tANI_U8 *value = command;
tANI_U8 lfrMode = CFG_LFR_FEATURE_ENABLED_DEFAULT;
if (pHddCtx->concurrency_mode == VOS_STA_MON) {
hddLog(LOGE,
FL("Roaming is always disabled in STA + MON concurrency"));
ret = -EINVAL;
goto exit;
}
ret = hdd_drv_cmd_validate(command, 11);
if (ret)
goto exit;
/* Move pointer to ahead of SETFASTROAM<delimiter> */
value = value + 12;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &lfrMode);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_LFR_FEATURE_ENABLED_MIN,
CFG_LFR_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
if ((lfrMode < CFG_LFR_FEATURE_ENABLED_MIN) ||
(lfrMode > CFG_LFR_FEATURE_ENABLED_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"lfr mode value %d is out of range"
" (Min: %d Max: %d)", lfrMode,
CFG_LFR_FEATURE_ENABLED_MIN,
CFG_LFR_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change lfr mode = %d", __func__, lfrMode);
pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled = lfrMode;
sme_UpdateIsFastRoamIniFeatureEnabled((tHalHandle)(pHddCtx->hHal), lfrMode);
}
#endif
#ifdef WLAN_FEATURE_VOWIFI_11R
else if (strncmp(command, "SETFASTTRANSITION", 17) == 0)
{
tANI_U8 *value = command;
tANI_U8 ft = CFG_FAST_TRANSITION_ENABLED_NAME_DEFAULT;
ret = hdd_drv_cmd_validate(command, 17);
if (ret)
goto exit;
/* Move pointer to ahead of SETFASTROAM<delimiter> */
value = value + 18;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &ft);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
ret = -EINVAL;
goto exit;
}
if ((ft < CFG_FAST_TRANSITION_ENABLED_NAME_MIN) ||
(ft > CFG_FAST_TRANSITION_ENABLED_NAME_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"ft mode value %d is out of range"
" (Min: %d Max: %d)", ft,
CFG_FAST_TRANSITION_ENABLED_NAME_MIN,
CFG_FAST_TRANSITION_ENABLED_NAME_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change ft mode = %d", __func__, ft);
pHddCtx->cfg_ini->isFastTransitionEnabled = ft;
sme_UpdateFastTransitionEnabled((tHalHandle)(pHddCtx->hHal), ft);
}
else if (strncmp(command, "SETDFSSCANMODE", 14) == 0)
{
tANI_U8 *value = command;
tANI_U8 dfsScanMode = DFS_CHNL_SCAN_ENABLED_NORMAL;
ret = hdd_drv_cmd_validate(command, 14);
if (ret)
goto exit;
/* Move pointer to ahead of SETDFSSCANMODE<delimiter> */
value = value + 15;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &dfsScanMode);
if (ret < 0)
{
/* If the input value is greater than max value of
datatype, then also kstrtou8 fails
*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ENABLE_DFS_CHNL_SCAN_MIN,
CFG_ENABLE_DFS_CHNL_SCAN_MAX);
ret = -EINVAL;
goto exit;
}
if ((dfsScanMode < CFG_ENABLE_DFS_CHNL_SCAN_MIN) ||
(dfsScanMode > CFG_ENABLE_DFS_CHNL_SCAN_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"dfsScanMode value %d is out of range"
" (Min: %d Max: %d)", dfsScanMode,
CFG_ENABLE_DFS_CHNL_SCAN_MIN,
CFG_ENABLE_DFS_CHNL_SCAN_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set DFS Scan Mode = %d",
__func__, dfsScanMode);
ret = wlan_hdd_handle_dfs_chan_scan(pHddCtx, dfsScanMode);
}
else if (strncmp(command, "GETDFSSCANMODE", 14) == 0)
{
tANI_U8 dfsScanMode = sme_GetDFSScanMode(pHddCtx->hHal);
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d", command, dfsScanMode);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "FASTREASSOC", 11) == 0)
{
ret = wlan_hdd_handle_fastreassoc(pAdapter, command);
if (!ret)
goto exit;
}
#endif
#ifdef FEATURE_WLAN_ESE
else if (strncmp(command, "SETCCXMODE", 10) == 0)
{
tANI_U8 *value = command;
tANI_U8 eseMode = CFG_ESE_FEATURE_ENABLED_DEFAULT;
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (sme_getIsEseFeatureEnabled((tHalHandle)(pHddCtx->hHal)) &&
hdd_is_okc_mode_enabled(pHddCtx) &&
sme_getIsFtFeatureEnabled((tHalHandle)(pHddCtx->hHal)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: OKC/ESE/11R are supported simultaneously"
" hence this operation is not permitted!", __func__);
ret = -EPERM;
goto exit;
}
/* Move pointer to ahead of SETCCXMODE<delimiter> */
value = value + 11;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &eseMode);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_ESE_FEATURE_ENABLED_MIN,
CFG_ESE_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
if ((eseMode < CFG_ESE_FEATURE_ENABLED_MIN) ||
(eseMode > CFG_ESE_FEATURE_ENABLED_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Ese mode value %d is out of range"
" (Min: %d Max: %d)", eseMode,
CFG_ESE_FEATURE_ENABLED_MIN,
CFG_ESE_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change ese mode = %d", __func__, eseMode);
pHddCtx->cfg_ini->isEseIniFeatureEnabled = eseMode;
sme_UpdateIsEseFeatureEnabled((tHalHandle)(pHddCtx->hHal), eseMode);
}
#endif
else if (strncmp(command, "SETROAMSCANCONTROL", 18) == 0)
{
tANI_U8 *value = command;
tANI_BOOLEAN roamScanControl = 0;
ret = hdd_drv_cmd_validate(command, 18);
if (ret)
goto exit;
/* Move pointer to ahead of SETROAMSCANCONTROL<delimiter> */
value = value + 19;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &roamScanControl);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed ", __func__);
ret = -EINVAL;
goto exit;
}
if (0 != roamScanControl)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"roam scan control invalid value = %d",
roamScanControl);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set roam scan control = %d", __func__, roamScanControl);
sme_SetRoamScanControl((tHalHandle)(pHddCtx->hHal), roamScanControl);
}
#ifdef FEATURE_WLAN_OKC
else if (strncmp(command, "SETOKCMODE", 10) == 0)
{
tANI_U8 *value = command;
tANI_U8 okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (sme_getIsEseFeatureEnabled((tHalHandle)(pHddCtx->hHal)) &&
hdd_is_okc_mode_enabled(pHddCtx) &&
sme_getIsFtFeatureEnabled((tHalHandle)(pHddCtx->hHal)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: OKC/ESE/11R are supported simultaneously"
" hence this operation is not permitted!", __func__);
ret = -EPERM;
goto exit;
}
/* Move pointer to ahead of SETOKCMODE<delimiter> */
value = value + 11;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &okcMode);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
CFG_OKC_FEATURE_ENABLED_MIN,
CFG_OKC_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
if ((okcMode < CFG_OKC_FEATURE_ENABLED_MIN) ||
(okcMode > CFG_OKC_FEATURE_ENABLED_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Okc mode value %d is out of range"
" (Min: %d Max: %d)", okcMode,
CFG_OKC_FEATURE_ENABLED_MIN,
CFG_OKC_FEATURE_ENABLED_MAX);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to change okc mode = %d", __func__, okcMode);
pHddCtx->cfg_ini->isOkcIniFeatureEnabled = okcMode;
}
#endif /* FEATURE_WLAN_OKC */
else if (strncmp(command, "GETROAMSCANCONTROL", 18) == 0)
{
tANI_BOOLEAN roamScanControl = sme_GetRoamScanControl((tHalHandle)(pHddCtx->hHal));
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d",
command, roamScanControl);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
#ifdef WLAN_FEATURE_PACKET_FILTERING
else if (strncmp(command, "ENABLE_PKTFILTER_IPV6", 21) == 0)
{
tANI_U8 filterType = 0;
tANI_U8 *value = command;
ret = hdd_drv_cmd_validate(command, 21);
if (ret)
goto exit;
/* Move pointer to ahead of ENABLE_PKTFILTER_IPV6<delimiter> */
value = value + 22;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &filterType);
if (ret < 0)
{
/* If the input value is greater than max value of datatype,
* then also kstrtou8 fails
*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range ", __func__);
ret = -EINVAL;
goto exit;
}
if (filterType != 0 && filterType != 1)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Accepted Values are 0 and 1 ", __func__);
ret = -EINVAL;
goto exit;
}
wlan_hdd_setIPv6Filter(WLAN_HDD_GET_CTX(pAdapter), filterType,
pAdapter->sessionId);
}
#endif
else if (strncmp(command, "BTCOEXMODE", 10) == 0 )
{
char *dhcpPhase;
int ret;
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
dhcpPhase = command + 11;
if ('1' == *dhcpPhase)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("send DHCP START indication"));
pHddCtx->btCoexModeSet = TRUE;
ret = wlan_hdd_scan_abort(pAdapter);
if (ret < 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("failed to abort existing scan %d"), ret);
}
sme_DHCPStartInd(pHddCtx->hHal, pAdapter->device_mode,
pAdapter->sessionId);
}
else if ('2' == *dhcpPhase)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("send DHCP STOP indication"));
pHddCtx->btCoexModeSet = FALSE;
sme_DHCPStopInd(pHddCtx->hHal, pAdapter->device_mode,
pAdapter->sessionId);
}
}
else if (strncmp(command, "SCAN-ACTIVE", 11) == 0)
{
hddLog(LOG1,
FL("making default scan to ACTIVE"));
pHddCtx->scan_info.scan_mode = eSIR_ACTIVE_SCAN;
}
else if (strncmp(command, "SCAN-PASSIVE", 12) == 0)
{
hddLog(LOG1,
FL("making default scan to PASSIVE"));
pHddCtx->scan_info.scan_mode = eSIR_PASSIVE_SCAN;
}
else if (strncmp(command, "GETDWELLTIME", 12) == 0)
{
hdd_config_t *pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini;
char extra[32];
tANI_U8 len = 0;
memset(extra, 0, sizeof(extra));
ret = hdd_get_dwell_time(pCfg, command, extra, sizeof(extra), &len);
len = VOS_MIN(priv_data.total_len, len + 1);
if (ret != 0 || copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
ret = len;
}
else if (strncmp(command, "SETDWELLTIME", 12) == 0)
{
ret = hdd_set_dwell_time(pAdapter, command);
}
else if (strncmp(command, "BTCGETDWELLTIME", 15) == 0)
{
hdd_config_t *pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini;
char extra[32];
tANI_U8 len = 0;
if (hdd_drv_cmd_validate(command, 15)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
memset(extra, 0, sizeof(extra));
ret = hdd_btc_get_dwell_time(pCfg, command, extra,
sizeof(extra), &len);
len = VOS_MIN(priv_data.total_len, len + 1);
if (ret != 0 || copy_to_user(priv_data.buf, &extra, len)) {
hddLog(LOGE, FL("Failed to copy data to user buffer"));
ret = -EFAULT;
goto exit;
}
ret = len;
}
else if (strncmp(command, "BTCSETDWELLTIME", 15) == 0)
{
if (hdd_drv_cmd_validate(command, 15)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
ret = hdd_btc_set_dwell_time(pAdapter, command);
}
#ifdef WLAN_AP_STA_CONCURRENCY
else if (strncmp(command, "CONCGETDWELLTIME", 16) == 0)
{
hdd_config_t *pCfg = (WLAN_HDD_GET_CTX(pAdapter))->cfg_ini;
char extra[32];
tANI_U8 len = 0;
if (hdd_drv_cmd_validate(command, 16)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
memset(extra, 0, sizeof(extra));
ret = hdd_conc_get_dwell_time(pCfg, command, extra,
sizeof(extra), &len);
len = VOS_MIN(priv_data.total_len, len + 1);
if (ret != 0 || copy_to_user(priv_data.buf, &extra, len)) {
hddLog(LOGE, FL("Failed to copy data to user buffer"));
ret = -EFAULT;
goto exit;
}
ret = len;
}
else if (strncmp(command, "CONCSETDWELLTIME", 16) == 0)
{
if (hdd_drv_cmd_validate(command, 16)) {
hddLog(LOGE, FL("Invalid driver command"));
return -EINVAL;
}
ret = hdd_conc_set_dwell_time(pAdapter, command);
}
#endif
else if ( strncasecmp(command, "MIRACAST", 8) == 0 )
{
tANI_U8 filterType = 0;
tANI_U8 *value;
ret = hdd_drv_cmd_validate(command, 8);
if (ret)
goto exit;
value = command + 9;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &filterType);
if (ret < 0)
{
/* If the input value is greater than max value of datatype,
* then also kstrtou8 fails
*/
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range ", __func__);
ret = -EINVAL;
goto exit;
}
if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL ) ||
(filterType > WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Accepted Values are 0 to 2. 0-Disabled, 1-Source,"
" 2-Sink ", __func__);
ret = -EINVAL;
goto exit;
}
//Filtertype value should be either 0-Disabled, 1-Source, 2-sink
pHddCtx->drvr_miracast = filterType;
pScanInfo = &pHddCtx->scan_info;
if (filterType && pScanInfo != NULL &&
pHddCtx->scan_info.mScanPending)
{
/*Miracast Session started. Abort Scan */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s, Aborting Scan For Miracast",__func__);
hdd_abort_mac_scan(pHddCtx, pScanInfo->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
hdd_tx_rx_pkt_cnt_stat_timer_handler(pHddCtx);
sme_SetMiracastMode(pHddCtx->hHal, pHddCtx->drvr_miracast);
}
else if (strncmp(command, "SETMCRATE", 9) == 0)
{
tANI_U8 *value = command;
int targetRate;
tSirRateUpdateInd *rateUpdate;
eHalStatus status;
ret = hdd_drv_cmd_validate(command, 9);
if (ret)
goto exit;
/* Only valid for SAP mode */
if (WLAN_HDD_SOFTAP != pAdapter->device_mode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: SAP mode is not running", __func__);
ret = -EFAULT;
goto exit;
}
/* Move pointer to ahead of SETMCRATE<delimiter> */
/* input value is in units of hundred kbps */
value = value + 10;
/* Convert the value from ascii to integer, decimal base */
ret = kstrtouint(value, 10, &targetRate);
rateUpdate = (tSirRateUpdateInd *)vos_mem_malloc(sizeof(tSirRateUpdateInd));
if (NULL == rateUpdate)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SETMCRATE indication alloc fail", __func__);
ret = -EFAULT;
goto exit;
}
vos_mem_zero(rateUpdate, sizeof(tSirRateUpdateInd ));
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"MC Target rate %d", targetRate);
/* Ignore unicast */
rateUpdate->ucastDataRate = -1;
rateUpdate->mcastDataRate24GHz = targetRate;
rateUpdate->mcastDataRate5GHz = targetRate;
rateUpdate->mcastDataRate24GHzTxFlag = 0;
rateUpdate->mcastDataRate5GHzTxFlag = 0;
status = sme_SendRateUpdateInd(pHddCtx->hHal, rateUpdate);
if (eHAL_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SET_MC_RATE failed", __func__);
vos_mem_free(rateUpdate);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "MAXTXPOWER", 10) == 0)
{
int status;
int txPower;
eHalStatus smeStatus;
tANI_U8 *value = command;
tSirMacAddr bssid = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
tSirMacAddr selfMac = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
status = hdd_parse_setmaxtxpower_command(value, &txPower);
if (status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid MAXTXPOWER command ");
ret = -EINVAL;
goto exit;
}
hddLog(VOS_TRACE_LEVEL_INFO, "max tx power %d selfMac: "
MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
txPower, MAC_ADDR_ARRAY(selfMac),
MAC_ADDR_ARRAY(bssid));
smeStatus = sme_SetMaxTxPower((tHalHandle)(pHddCtx->hHal),
bssid, selfMac, txPower) ;
if( smeStatus != eHAL_STATUS_SUCCESS )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:Set max tx power failed",
__func__);
ret = -EINVAL;
goto exit;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Set max tx power success",
__func__);
}
#ifdef FEATURE_WLAN_BATCH_SCAN
else if (strncmp(command, "WLS_BATCHING", 12) == 0)
{
ret = hdd_handle_batch_scan_ioctl(pAdapter, &priv_data, command);
}
#endif
#ifdef WLAN_FEATURE_RMC
else if ((strncasecmp(command, "SETIBSSBEACONOUIDATA", 20) == 0) &&
(WLAN_HDD_IBSS == pAdapter->device_mode))
{
int i = 0;
tANI_U8 *ibss_ie;
tANI_U32 command_len;
tANI_U8 *value = command;
tHalHandle hHal = pHddCtx->hHal;
tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
tANI_U32 ibss_ie_length;
tANI_U32 len, present;
tANI_U8 *addIE;
tANI_U8 *addIEData;
hddLog(LOG1,
FL(" received command %s"),((char *) value));
/* validate argument of command */
if (strlen(value) <= 21)
{
hddLog(LOGE,
FL("No arguements in command length %zu"), strlen(value));
ret = -EFAULT;
goto exit;
}
/* moving to arguments of commands */
value = value + 21;
command_len = strlen(value);
/* oui_data can't be less than 3 bytes */
if (command_len <= (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH))
{
hddLog(LOGE,
FL("Invalid SETIBSSBEACONOUIDATA command length %d"),
command_len);
ret = -EFAULT;
goto exit;
}
ibss_ie = vos_mem_malloc(command_len);
if (!ibss_ie) {
hddLog(LOGE,
FL("Could not allocate memory for command length %d"),
command_len);
ret = -ENOMEM;
goto exit;
}
vos_mem_zero(ibss_ie, command_len);
ibss_ie_length = hdd_parse_set_ibss_oui_data_command(value, ibss_ie,
command_len);
if (ibss_ie_length < (2 * WLAN_HDD_IBSS_MIN_OUI_DATA_LENGTH)) {
hddLog(LOGE, FL("Could not parse command %s return length %d"),
value, ibss_ie_length);
ret = -EFAULT;
vos_mem_free(ibss_ie);
goto exit;
}
hddLog(LOG1, FL("ibss_ie length %d ibss_ie:"), ibss_ie_length);
while (i < ibss_ie_length)
hddLog(LOG1, FL("0x%x"), ibss_ie[i++]);
/* Populate Vendor IE in Beacon */
if ((ccmCfgGetInt(hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG,
&present)) != eHAL_STATUS_SUCCESS)
{
hddLog(LOGE,
FL("unable to ftch WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
goto exit;
}
addIE = vos_mem_malloc(WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN);
if (!addIE) {
hddLog(LOGE,
FL("Could not allocate memory for command length %d"),
command_len);
vos_mem_free(ibss_ie);
ret = -ENOMEM;
goto exit;
}
vos_mem_zero(addIE, WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN);
if (present)
{
if ((wlan_cfgGetStrLen(pMac,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, &len)) != eSIR_SUCCESS)
{
hddLog(LOGE,
FL("unable to fetch WNI_CFG_PROBE_RSP_BCN_ADDNIE_LEN"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
vos_mem_free(addIE);
goto exit;
}
if (len <= WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN && len &&
((len + ibss_ie_length) <=
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA_LEN))
{
if ((ccmCfgGetStr(hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, addIE, &len))
!= eHAL_STATUS_SUCCESS)
{
hddLog(LOGE,
FL("unable to fetch WNI_PROBE_RSP_BCN_ADDNIE_DATA"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
vos_mem_free(addIE);
goto exit;
}
else
{
/* Curruntly only WPA IE is added before Vendor IE
* so we can blindly place the Vendor IE after WPA
* IE. If no WPA IE found replace all with Vendor IE.
*/
len = hdd_find_ibss_wpa_ie_pos(addIE, len);
}
}
else
{
hddLog(LOGE,
FL("IE len exceed limit len %d,ibss_ie_length %d "),
len, ibss_ie_length);
ret = -EFAULT;
vos_mem_free(addIE);
vos_mem_free(ibss_ie);
goto exit;
}
}
else {
len = 0;
}
vos_mem_copy (addIE + len , ibss_ie, ibss_ie_length);
len += ibss_ie_length;
if (ccmCfgSetStr(hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_DATA, addIE, len, NULL,
eANI_BOOLEAN_FALSE) != eHAL_STATUS_SUCCESS)
{
hddLog(LOGE,
FL("unable to set WNI_CFG_PRBE_RSP_BCN_ADDNIE_DATA"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
vos_mem_free(addIE);
goto exit;
}
vos_mem_free(addIE);
if (ccmCfgSetInt(hHal,
WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG, 1,NULL,
eANI_BOOLEAN_FALSE) != eHAL_STATUS_SUCCESS)
{
hddLog(LOGE,
FL("unble to set WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
goto exit;
}
/* Populate Vendor IE in probe resp */
if ((ccmCfgGetInt(hHal,
WNI_CFG_PROBE_RSP_ADDNIE_FLAG,
&present)) != eHAL_STATUS_SUCCESS)
{
hddLog(LOGE,
FL("unable to fetch WNI_CFG_PROBE_RSP_ADDNIE_FLAG"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
goto exit;
}
addIEData = vos_mem_malloc(WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN);
if (!addIEData) {
hddLog(LOGE,
FL("Could not allocate memory for command length %d"),
command_len);
vos_mem_free(ibss_ie);
ret = -ENOMEM;
goto exit;
}
vos_mem_zero(addIEData, WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN);
if (present) {
if (eSIR_SUCCESS != wlan_cfgGetStrLen(pMac,
WNI_CFG_PROBE_RSP_ADDNIE_DATA1, &len)) {
hddLog(LOGE,
FL("unable to fetch WNI_CFG_PROBE_RSP_ADDNIE_DATA1"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
vos_mem_free(addIEData);
goto exit;
}
if (len < WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN && len &&
(ibss_ie_length + len) <=
WNI_CFG_PROBE_RSP_ADDNIE_DATA1_LEN) {
if ((ccmCfgGetStr(hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA1, addIEData, &len))
!= eHAL_STATUS_SUCCESS) {
hddLog(LOGE,
FL("unable fetch WNI_CFG_PROBE_RSP_ADDNIE_DATA1"));
ret = -EFAULT;
vos_mem_free(ibss_ie);
vos_mem_free(addIEData);
goto exit;
}
else {
/* Curruntly only WPA IE is added before Vendor IE
* so we can blindly place the Vendor IE after WPA
* IE. If no WPA IE found replace all with Vendor IE.
*/
len = hdd_find_ibss_wpa_ie_pos(addIEData, len);
}
}
else
{
hddLog(LOGE,
FL("IE len exceed limit len %d,ibss_ie_length %d "),
len, ibss_ie_length);
ret = -EFAULT;
vos_mem_free(addIEData);
vos_mem_free(ibss_ie);
goto exit;
}
} /* probe rsp ADD IE present */
else {
/* probe rsp add IE is not present */
len = 0;
}
vos_mem_copy(addIEData +len , ibss_ie, ibss_ie_length);
len += ibss_ie_length;
vos_mem_free(ibss_ie);
if (ccmCfgSetStr(hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA1,
(tANI_U8*)(addIEData),
len, NULL,
eANI_BOOLEAN_FALSE)
== eHAL_STATUS_FAILURE) {
hddLog(LOGE,
FL("unable to copy to WNI_CFG_PROBE_RSP_ADDNIE_DATA1"));
ret = -EFAULT;
vos_mem_free(addIEData);
goto exit;
}
vos_mem_free(addIEData);
if (ccmCfgSetInt(WLAN_HDD_GET_HAL_CTX(pAdapter),
WNI_CFG_PROBE_RSP_ADDNIE_FLAG, 1,NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE,
FL("unable to copy WNI_CFG_PROBE_RSP_ADDNIE_FLAG"));
ret = -EFAULT;
goto exit;
}
}
else if (strncasecmp(command, "SETRMCENABLE", 12) == 0)
{
tANI_U8 *value = command;
tANI_U8 ucRmcEnable = 0;
int status;
if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
(WLAN_HDD_SOFTAP != pAdapter->device_mode))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Received SETRMCENABLE command in invalid mode %d "
"SETRMCENABLE command is only allowed in IBSS or SOFTAP mode",
pAdapter->device_mode);
ret = -EINVAL;
goto exit;
}
status = hdd_parse_setrmcenable_command(value, &ucRmcEnable);
if (status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid SETRMCENABLE command ");
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: ucRmcEnable %d ", __func__, ucRmcEnable);
if (TRUE == ucRmcEnable)
{
status = sme_EnableRMC( (tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId );
}
else if(FALSE == ucRmcEnable)
{
status = sme_DisableRMC( (tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId );
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid SETRMCENABLE command %d", ucRmcEnable);
ret = -EINVAL;
goto exit;
}
if (VOS_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: SETRMC %d failed status %d", __func__, ucRmcEnable,
status);
ret = -EINVAL;
goto exit;
}
}
else if (strncasecmp(command, "SETRMCACTIONPERIOD", 18) == 0)
{
tANI_U8 *value = command;
tANI_U32 uActionPeriod = 0;
int status;
if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
(WLAN_HDD_SOFTAP != pAdapter->device_mode))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Received SETRMC command in invalid mode %d "
"SETRMC command is only allowed in IBSS or SOFTAP mode",
pAdapter->device_mode);
ret = -EINVAL;
goto exit;
}
status = hdd_parse_setrmcactionperiod_command(value, &uActionPeriod);
if (status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid SETRMCACTIONPERIOD command ");
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: uActionPeriod %d ", __func__, uActionPeriod);
if (ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_RMC_ACTION_PERIOD_FREQUENCY,
uActionPeriod, NULL, eANI_BOOLEAN_FALSE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Could not set SETRMCACTIONPERIOD %d", __func__, uActionPeriod);
ret = -EINVAL;
goto exit;
}
}
else if (strncasecmp(command, "GETIBSSPEERINFOALL", 18) == 0)
{
/* Peer Info All Command */
int status = eHAL_STATUS_SUCCESS;
hdd_station_ctx_t *pHddStaCtx = NULL;
char *extra = NULL;
int idx = 0, length = 0;
v_MACADDR_t *macAddr;
v_U32_t txRateMbps = 0, numOfBytestoPrint = 0;
if (WLAN_HDD_IBSS == pAdapter->device_mode)
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: pAdapter is not valid for this device mode",
__func__);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received GETIBSSPEERINFOALL Command", __func__);
/* Handle the command */
status = hdd_cfg80211_get_ibss_peer_info_all(pAdapter);
if (VOS_STATUS_SUCCESS == status)
{
/* The variable extra needed to be allocated on the heap since
* amount of memory required to copy the data for 32 devices
* exceeds the size of 1024 bytes of default stack size. On
* 64 bit devices, the default max stack size of 2048 bytes
*/
extra = kmalloc(WLAN_MAX_BUF_SIZE, GFP_KERNEL);
if (NULL == extra)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:kmalloc failed", __func__);
ret = -EINVAL;
goto exit;
}
/* Copy number of stations */
length = scnprintf( extra, WLAN_MAX_BUF_SIZE, "%d ",
pHddStaCtx->ibss_peer_info.numIBSSPeers);
numOfBytestoPrint = length;
for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numIBSSPeers; idx++)
{
macAddr =
hdd_wlan_get_ibss_mac_addr_from_staid(pAdapter,
pHddStaCtx->ibss_peer_info.ibssPeerList[idx].staIdx);
if (NULL != macAddr)
{
txRateMbps =
((pHddStaCtx->ibss_peer_info.ibssPeerList[idx].txRate)*500*1000)/1000000;
length += scnprintf( (extra + length), WLAN_MAX_BUF_SIZE - length,
"%02x:%02x:%02x:%02x:%02x:%02x %d %d ",
macAddr->bytes[0], macAddr->bytes[1], macAddr->bytes[2],
macAddr->bytes[3], macAddr->bytes[4], macAddr->bytes[5],
(int)txRateMbps,
(int)pHddStaCtx->ibss_peer_info.ibssPeerList[idx].rssi);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: MAC ADDR is NULL for staIdx: %d", __func__,
pHddStaCtx->ibss_peer_info.ibssPeerList[idx].staIdx);
}
/*
* VOS_TRACE() macro has limitation of 512 bytes for the print
* buffer. Hence printing the data in two chunks. The first chunk
* will have the data for 16 devices and the second chunk will
* have the rest.
*/
if (idx < NUM_OF_STA_DATA_TO_PRINT)
{
numOfBytestoPrint = length;
}
}
/*
* Copy the data back into buffer, if the data to copy is
* morethan 512 bytes than we will split the data and do
* it in two shots
*/
if (copy_to_user(priv_data.buf, extra, numOfBytestoPrint))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Copy into user data buffer failed ", __func__);
ret = -EFAULT;
kfree(extra);
goto exit;
}
priv_data.buf[numOfBytestoPrint] = '\0';
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"%s", priv_data.buf);
if (length > numOfBytestoPrint)
{
if (copy_to_user(priv_data.buf + numOfBytestoPrint,
extra + numOfBytestoPrint,
length - numOfBytestoPrint + 1))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Copy into user data buffer failed ", __func__);
ret = -EFAULT;
kfree(extra);
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"%s", &priv_data.buf[numOfBytestoPrint]);
}
/* Free temporary buffer */
kfree(extra);
}
else
{
/* Command failed, log error */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: GETIBSSPEERINFOALL command failed with status code %d",
__func__, status);
ret = -EINVAL;
goto exit;
}
ret = 0;
}
else if(strncasecmp(command, "GETIBSSPEERINFO", 15) == 0)
{
/* Peer Info <Peer Addr> command */
tANI_U8 *value = command;
VOS_STATUS status;
hdd_station_ctx_t *pHddStaCtx = NULL;
char extra[128] = { 0 };
v_U32_t length = 0;
v_U8_t staIdx = 0;
v_U32_t txRateMbps = 0;
v_MACADDR_t peerMacAddr;
if (WLAN_HDD_IBSS == pAdapter->device_mode)
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
}
else
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: pAdapter is not valid for this device mode",
__func__);
ret = -EINVAL;
goto exit;
}
/* if there are no peers, no need to continue with the command */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received GETIBSSPEERINFO Command", __func__);
if (eConnectionState_IbssConnected != pHddStaCtx->conn_info.connState)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s:No IBSS Peers coalesced", __func__);
ret = -EINVAL;
goto exit;
}
/* Parse the incoming command buffer */
status = hdd_parse_get_ibss_peer_info(value, &peerMacAddr);
if (VOS_STATUS_SUCCESS != status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid GETIBSSPEERINFO command", __func__);
ret = -EINVAL;
goto exit;
}
/* Get station index for the peer mac address */
hdd_Ibss_GetStaId(pHddStaCtx, &peerMacAddr, &staIdx);
if (staIdx > HDD_MAX_NUM_IBSS_STA)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Invalid StaIdx %d returned", __func__, staIdx);
ret = -EINVAL;
goto exit;
}
/* Handle the command */
status = hdd_cfg80211_get_ibss_peer_info(pAdapter, staIdx);
if (VOS_STATUS_SUCCESS == status)
{
v_U32_t txRate = pHddStaCtx->ibss_peer_info.ibssPeerList[0].txRate;
txRateMbps = (txRate * 500 * 1000)/1000000;
length = scnprintf( extra, sizeof(extra), "%d %d", (int)txRateMbps,
(int)pHddStaCtx->ibss_peer_info.ibssPeerList[0].rssi);
/* Copy the data back into buffer */
if (copy_to_user(priv_data.buf, &extra, length+ 1))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: copy data to user buffer failed GETIBSSPEERINFO command",
__func__);
ret = -EFAULT;
goto exit;
}
}
else
{
/* Command failed, log error */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: GETIBSSPEERINFO command failed with status code %d",
__func__, status);
ret = -EINVAL;
goto exit;
}
/* Success ! */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"%s", priv_data.buf);
ret = 0;
}
else if (strncasecmp(command, "SETRMCTXRATE", 12) == 0)
{
tANI_U8 *value = command;
tANI_U32 uRate = 0;
tTxrateinfoflags txFlags = 0;
tSirRateUpdateInd *rateUpdateParams;
int status;
if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
(WLAN_HDD_SOFTAP != pAdapter->device_mode))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Received SETRMCTXRATE command in invalid mode %d "
"SETRMC command is only allowed in IBSS or SOFTAP mode",
pAdapter->device_mode);
ret = -EINVAL;
goto exit;
}
status = hdd_parse_setrmcrate_command(value, &uRate, &txFlags);
if (status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid SETRMCTXRATE command ");
ret = -EINVAL;
goto exit;
}
rateUpdateParams = vos_mem_malloc(sizeof(tSirRateUpdateInd));
if (NULL == rateUpdateParams)
{
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: uRate %d ", __func__, uRate);
vos_mem_zero(rateUpdateParams, sizeof(tSirRateUpdateInd ));
/* -1 implies ignore this param */
rateUpdateParams->ucastDataRate = -1;
/*
* Fill the user specifieed RMC rate param
* and the derived tx flags.
*/
rateUpdateParams->rmcDataRate = uRate;
rateUpdateParams->rmcDataRateTxFlag = txFlags;
status = sme_SendRateUpdateInd((tHalHandle)(pHddCtx->hHal), rateUpdateParams);
}
else if (strncasecmp(command, "SETIBSSTXFAILEVENT", 18) == 0 )
{
char *value;
tANI_U8 tx_fail_count = 0;
tANI_U16 pid = 0;
value = command;
ret = hdd_ParseIBSSTXFailEventParams(value, &tx_fail_count, &pid);
if (0 != ret)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Failed to parse SETIBSSTXFAILEVENT arguments",
__func__);
goto exit;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: tx_fail_cnt=%hhu, pid=%hu",
__func__, tx_fail_count, pid);
if (0 == tx_fail_count)
{
// Disable TX Fail Indication
if (eHAL_STATUS_SUCCESS ==
sme_TXFailMonitorStartStopInd(pHddCtx->hHal,
tx_fail_count,
NULL))
{
cesium_pid = 0;
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to disable TX Fail Event ", __func__);
ret = -EINVAL;
}
}
else
{
if (eHAL_STATUS_SUCCESS ==
sme_TXFailMonitorStartStopInd(pHddCtx->hHal,
tx_fail_count,
(void*)hdd_tx_fail_ind_callback))
{
cesium_pid = pid;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Registered Cesium pid %u", __func__,
cesium_pid);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to enable TX Fail Monitoring", __func__);
ret = -EINVAL;
}
}
}
#endif /* WLAN_FEATURE_RMC */
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
else if (strncmp(command, "SETCCXROAMSCANCHANNELS", 22) == 0)
{
tANI_U8 *value = command;
tANI_U8 ChannelList[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U8 numChannels = 0;
eHalStatus status = eHAL_STATUS_SUCCESS;
status = hdd_parse_channellist(value, ChannelList, &numChannels);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse channel list information", __func__);
ret = -EINVAL;
goto exit;
}
if (numChannels > WNI_CFG_VALID_CHANNEL_LIST_LEN)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: number of channels (%d) supported exceeded max (%d)", __func__,
numChannels, WNI_CFG_VALID_CHANNEL_LIST_LEN);
ret = -EINVAL;
goto exit;
}
status = sme_SetEseRoamScanChannelList((tHalHandle)(pHddCtx->hHal),
ChannelList,
numChannels);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to update channel list information", __func__);
ret = -EINVAL;
goto exit;
}
}
else if (strncmp(command, "GETTSMSTATS", 11) == 0)
{
tANI_U8 *value = command;
char extra[128] = {0};
int len = 0;
tANI_U8 tid = 0;
hdd_station_ctx_t *pHddStaCtx = NULL;
tAniTrafStrmMetrics tsmMetrics;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
ret = hdd_drv_cmd_validate(command, 11);
if (ret)
goto exit;
/* if not associated, return error */
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, "%s:Not associated!",__func__);
ret = -EINVAL;
goto exit;
}
/* Move pointer to ahead of GETTSMSTATS<delimiter> */
value = value + 12;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &tid);
if (ret < 0)
{
/* If the input value is greater than max value of datatype, then also
kstrtou8 fails */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: kstrtou8 failed range [%d - %d]", __func__,
TID_MIN_VALUE,
TID_MAX_VALUE);
ret = -EINVAL;
goto exit;
}
if ((tid < TID_MIN_VALUE) || (tid > TID_MAX_VALUE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"tid value %d is out of range"
" (Min: %d Max: %d)", tid,
TID_MIN_VALUE,
TID_MAX_VALUE);
ret = -EINVAL;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to get tsm stats tid = %d", __func__, tid);
if (VOS_STATUS_SUCCESS != hdd_get_tsm_stats(pAdapter, tid, &tsmMetrics))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to get tsm stats", __func__);
ret = -EFAULT;
goto exit;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"UplinkPktQueueDly(%d)\n"
"UplinkPktQueueDlyHist[0](%d)\n"
"UplinkPktQueueDlyHist[1](%d)\n"
"UplinkPktQueueDlyHist[2](%d)\n"
"UplinkPktQueueDlyHist[3](%d)\n"
"UplinkPktTxDly(%u)\n"
"UplinkPktLoss(%d)\n"
"UplinkPktCount(%d)\n"
"RoamingCount(%d)\n"
"RoamingDly(%d)", tsmMetrics.UplinkPktQueueDly,
tsmMetrics.UplinkPktQueueDlyHist[0],
tsmMetrics.UplinkPktQueueDlyHist[1],
tsmMetrics.UplinkPktQueueDlyHist[2],
tsmMetrics.UplinkPktQueueDlyHist[3],
tsmMetrics.UplinkPktTxDly, tsmMetrics.UplinkPktLoss,
tsmMetrics.UplinkPktCount, tsmMetrics.RoamingCount, tsmMetrics.RoamingDly);
/* Output TSM stats is of the format
GETTSMSTATS [PktQueueDly] [PktQueueDlyHist[0]]:[PktQueueDlyHist[1]] ...[RoamingDly]
eg., GETTSMSTATS 10 1:0:0:161 20 1 17 8 39800 */
len = scnprintf(extra, sizeof(extra), "%s %d %d:%d:%d:%d %u %d %d %d %d", command,
tsmMetrics.UplinkPktQueueDly, tsmMetrics.UplinkPktQueueDlyHist[0],
tsmMetrics.UplinkPktQueueDlyHist[1], tsmMetrics.UplinkPktQueueDlyHist[2],
tsmMetrics.UplinkPktQueueDlyHist[3], tsmMetrics.UplinkPktTxDly,
tsmMetrics.UplinkPktLoss, tsmMetrics.UplinkPktCount, tsmMetrics.RoamingCount,
tsmMetrics.RoamingDly);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
}
else if (strncmp(command, "SETCCKMIE", 9) == 0)
{
tANI_U8 *value = command;
tANI_U8 *cckmIe = NULL;
tANI_U8 cckmIeLen = 0;
eHalStatus status = eHAL_STATUS_SUCCESS;
status = hdd_parse_get_cckm_ie(value, &cckmIe, &cckmIeLen);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse cckm ie data", __func__);
ret = -EINVAL;
goto exit;
}
if (cckmIeLen > DOT11F_IE_RSN_MAX_LEN)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: CCKM Ie input length is more than max[%d]", __func__,
DOT11F_IE_RSN_MAX_LEN);
vos_mem_free(cckmIe);
cckmIe = NULL;
ret = -EINVAL;
goto exit;
}
sme_SetCCKMIe((tHalHandle)(pHddCtx->hHal), pAdapter->sessionId, cckmIe, cckmIeLen);
vos_mem_free(cckmIe);
cckmIe = NULL;
}
else if (strncmp(command, "CCXBEACONREQ", 12) == 0)
{
tANI_U8 *value = command;
tCsrEseBeaconReq eseBcnReq;
eHalStatus status = eHAL_STATUS_SUCCESS;
status = hdd_parse_ese_beacon_req(value, &eseBcnReq);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse ese beacon req", __func__);
ret = -EINVAL;
goto exit;
}
if (!hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("Not associated"));
hdd_indicateEseBcnReportNoResults (pAdapter,
eseBcnReq.bcnReq[0].measurementToken,
0x02, //BIT(1) set for measurement done
0); // no BSS
goto exit;
}
status = sme_SetEseBeaconRequest((tHalHandle)(pHddCtx->hHal), pAdapter->sessionId, &eseBcnReq);
if (eHAL_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_SetEseBeaconRequest failed (%d)", __func__, status);
ret = -EINVAL;
goto exit;
}
}
#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
else if (strncmp(command, "GETBCNMISSRATE", 14) == 0)
{
eHalStatus status;
char buf[32], len;
void *cookie;
struct hdd_request *request;
struct bcn_miss_rate_priv *priv;
static const struct hdd_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState)
{
hddLog(VOS_TRACE_LEVEL_WARN,
FL("GETBCNMISSRATE: STA is not in connected state"));
ret = -1;
goto exit;
}
request = hdd_request_alloc(&params);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request allocation failure"));
ret = -ENOMEM;
goto exit;
}
cookie = hdd_request_cookie(request);
priv = hdd_request_priv(request);
priv->bcn_miss_rate = -1;
status = sme_getBcnMissRate((tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId,
(void *)get_bcn_miss_rate_cb,
cookie);
if( eHAL_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("GETBCNMISSRATE: fail to post WDA cmd"));
ret = -EINVAL;
goto free_bcn_miss_rate_req;
}
ret = hdd_request_wait_for_response(request);
if(ret)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("failed to wait on bcnMissRateComp %d"), ret);
ret = -EINVAL;
goto free_bcn_miss_rate_req;
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("GETBCNMISSRATE: bcnMissRate: %d"), priv->bcn_miss_rate);
if (priv->bcn_miss_rate == -1) {
ret = -EFAULT;
goto free_bcn_miss_rate_req;
}
len = snprintf(buf, sizeof(buf), "GETBCNMISSRATE %d",
priv->bcn_miss_rate);
if (copy_to_user(priv_data.buf, &buf, len + 1))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto free_bcn_miss_rate_req;
}
ret = len;
free_bcn_miss_rate_req:
hdd_request_put(request);
}
#ifdef FEATURE_WLAN_TDLS
else if (strncmp(command, "TDLSSECONDARYCHANNELOFFSET", 26) == 0) {
tANI_U8 *value = command;
int set_value;
ret = hdd_drv_cmd_validate(command, 26);
if (ret)
goto exit;
/* Move pointer to ahead of TDLSOFFCH*/
value += 26;
if (!(sscanf(value, "%d", &set_value))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("No input identified"));
ret = -EINVAL;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Tdls offchannel offset:%d",
__func__, set_value);
ret = iw_set_tdlssecoffchanneloffset(pHddCtx, set_value);
if (ret < 0)
{
ret = -EINVAL;
goto exit;
}
} else if (strncmp(command, "TDLSOFFCHANNELMODE", 18) == 0) {
tANI_U8 *value = command;
int set_value;
ret = hdd_drv_cmd_validate(command, 18);
if (ret)
goto exit;
/* Move pointer to ahead of tdlsoffchnmode*/
value += 18;
ret = sscanf(value, "%d", &set_value);
if (ret != 1) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("No input identified"));
ret = -EINVAL;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Tdls offchannel mode:%d",
__func__, set_value);
ret = iw_set_tdlsoffchannelmode(pAdapter, set_value);
if (ret < 0)
{
ret = -EINVAL;
goto exit;
}
} else if (strncmp(command, "TDLSOFFCHANNEL", 14) == 0) {
tANI_U8 *value = command;
int set_value;
ret = hdd_drv_cmd_validate(command, 14);
if (ret)
goto exit;
/* Move pointer to ahead of TDLSOFFCH*/
value += 14;
ret = sscanf(value, "%d", &set_value);
if (ret != 1) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Wrong value is given for hdd_set_tdls_offchannel");
ret = -EINVAL;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Tdls offchannel num: %d",
__func__, set_value);
ret = iw_set_tdlsoffchannel(pHddCtx, set_value);
if (ret < 0)
{
ret = -EINVAL;
goto exit;
}
}
#endif
else if (strncmp(command, "GETFWSTATS", 10) == 0)
{
eHalStatus status;
char *buf = NULL;
char len;
tSirFwStatsResult *fwStatsRsp = &(pAdapter->fwStatsRsp),
*fw_stats_result;
tANI_U8 *ptr = command;
int stats;
void *cookie;
struct hdd_request *request;
struct fw_stats_priv *priv;
static const struct hdd_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
ret = hdd_drv_cmd_validate(command, 10);
if (ret)
goto exit;
stats = *(ptr + 11) - '0';
hddLog(VOS_TRACE_LEVEL_INFO, FL("stats = %d "),stats);
if (!IS_FEATURE_FW_STATS_ENABLE)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Get Firmware stats feature not supported"));
ret = -EINVAL;
goto exit;
}
if (FW_STATS_MAX <= stats || 0 >= stats)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL(" stats %d not supported"),stats);
ret = -EINVAL;
goto exit;
}
request = hdd_request_alloc(&params);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request allocation failure"));
ret = -ENOMEM;
goto exit;
}
cookie = hdd_request_cookie(request);
status = sme_GetFwStats( (tHalHandle)pHddCtx->hHal, stats,
cookie, hdd_fw_stats_cb);
if (eHAL_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL(" fail to post WDA cmd status = %d"), status);
ret = -EINVAL;
hdd_request_put(request);
goto exit;
}
ret = hdd_request_wait_for_response(request);
if (ret)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("failed to wait on GwtFwstats"));
ret = -EINVAL;
hdd_request_put(request);
goto exit;
}
priv = hdd_request_priv(request);
fw_stats_result = priv->fw_stats;
fwStatsRsp->type = 0;
if (NULL != fw_stats_result)
{
switch (fw_stats_result->type )
{
case FW_UBSP_STATS:
{
tSirUbspFwStats *stats =
&fwStatsRsp->fwStatsData.ubspStats;
memcpy(fwStatsRsp, fw_stats_result,
sizeof(tSirFwStatsResult));
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ubsp_enter_cnt = %d ubsp_jump_ddr_cnt = %d"),
stats->ubsp_enter_cnt,
stats->ubsp_jump_ddr_cnt);
}
break;
default:
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("No handling for stats type %d"),
fw_stats_result->type);
}
}
}
hdd_request_put(request);
if (fwStatsRsp->type)
{
buf = kmalloc(FW_STATE_RSP_LEN, GFP_KERNEL);
if (!buf)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL(" failed to allocate memory"));
ret = -ENOMEM;
goto exit;
}
switch( fwStatsRsp->type )
{
case FW_UBSP_STATS:
{
len = snprintf(buf, FW_STATE_RSP_LEN,
"GETFWSTATS: ubsp_enter_cnt %d ubsp_jump_ddr_cnt %d",
fwStatsRsp->fwStatsData.ubspStats.ubsp_enter_cnt,
fwStatsRsp->fwStatsData.ubspStats.ubsp_jump_ddr_cnt);
}
break;
default:
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL( "No handling for stats type %d"),fwStatsRsp->type);
ret = -EFAULT;
kfree(buf);
goto exit;
}
}
if (copy_to_user(priv_data.buf, buf, len + 1))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL(" failed to copy data to user buffer"));
ret = -EFAULT;
kfree(buf);
goto exit;
}
ret = len;
kfree(buf);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("failed to fetch the stats"));
ret = -EFAULT;
goto exit;
}
}
else if (strncasecmp(command, "SET_FCC_CHANNEL", 15) == 0)
{
ret = hdd_drv_cmd_validate(command, 15);
if (ret)
goto exit;
/*
* this command wld be called by user-space when it detects WLAN
* ON after airplane mode is set. When APM is set, WLAN turns off.
* But it can be turned back on. Otherwise; when APM is turned back
* off, WLAN wld turn back on. So at that point the command is
* expected to come down. 0 means disable, 1 means enable. The
* constraint is removed when parameter 1 is set or different
* country code is set
*/
ret = hdd_cmd_setFccChannel(pHddCtx, command, 15);
}
else if (strncasecmp(command, "DISABLE_CA_EVENT", 16) == 0)
{
ret = hdd_drv_cmd_validate(command, 16);
if (ret)
goto exit;
ret = hdd_enable_disable_ca_event(pHddCtx, command, 16);
}
/*
* command should be a string having format
* SET_DISABLE_CHANNEL_LIST <num of channels>
* <channels separated by spaces>
*/
else if (strncmp(command, "SET_DISABLE_CHANNEL_LIST", 24) == 0) {
tANI_U8 *ptr = command;
ret = hdd_drv_cmd_validate(command, 24);
if (ret)
goto exit;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
" Received Command to disable Channels %s",
__func__);
ret = hdd_parse_disable_chan_cmd(pAdapter, ptr);
if (ret)
goto exit;
}
else if (strncmp(command, "GET_DISABLE_CHANNEL_LIST", 24) == 0) {
char extra[512] = {0};
int max_len, copied_length;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
" Received Command to get disable Channels list %s",
__func__);
max_len = VOS_MIN(priv_data.total_len, sizeof(extra));
copied_length = hdd_get_disable_ch_list(pHddCtx, extra, max_len);
if (copied_length == 0) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("disable channel list are not yet programed"));
ret = -EINVAL;
goto exit;
}
if (copy_to_user(priv_data.buf, &extra, copied_length + 1)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("data:%s"), extra);
}
else {
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
pAdapter->sessionId, 0));
hddLog( VOS_TRACE_LEVEL_WARN, FL("Unsupported GUI command %s"),
command);
}
}
exit:
EXIT();
if (command)
{
kfree(command);
}
return ret;
}
#ifdef CONFIG_COMPAT
static int hdd_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_driver_command(pAdapter, &priv_data);
exit:
return ret;
}
#else /* CONFIG_COMPAT */
static int hdd_driver_compat_ioctl(hdd_adapter_t *pAdapter, struct ifreq *ifr)
{
/* will never be invoked */
return 0;
}
#endif /* CONFIG_COMPAT */
static int hdd_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_driver_command(pAdapter, &priv_data);
}
return ret;
}
int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int ret;
ENTER();
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter context is Null", __func__);
ret = -ENODEV;
goto exit;
}
if (dev != pAdapter->dev) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter/dev inconsistency", __func__);
ret = -ENODEV;
goto exit;
}
if ((!ifr) || (!ifr->ifr_data)) {
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 (is_compat_task())
ret = hdd_driver_compat_ioctl(pAdapter, ifr);
else
ret = hdd_driver_ioctl(pAdapter, ifr);
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: unknown ioctl %d",
__func__, cmd);
ret = -EINVAL;
break;
}
exit:
EXIT();
return ret;
}
int hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_ioctl(dev, ifr, cmd);
vos_ssr_unprotect(__func__);
return ret;
}
int hdd_mon_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
return 0;
}
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
/**---------------------------------------------------------------------------
\brief hdd_parse_ese_beacon_req() - Parse ese beacon request
This function parses the ese beacon request passed in the format
CCXBEACONREQ<space><Number of fields><space><Measurement token>
<space>Channel 1<space>Scan Mode <space>Meas Duration<space>Channel N
<space>Scan Mode N<space>Meas Duration N
if the Number of bcn req fields (N) does not match with the actual number of fields passed
then take N.
<Meas Token><Channel><Scan Mode> and <Meas Duration> are treated as one pair
For example, CCXBEACONREQ 2 1 1 1 30 2 44 0 40.
This function does not take care of removing duplicate channels from the list
\param - pValue Pointer to data
\param - pEseBcnReq output pointer to store parsed ie information
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static VOS_STATUS hdd_parse_ese_beacon_req(tANI_U8 *pValue,
tCsrEseBeaconReq *pEseBcnReq)
{
tANI_U8 *inPtr = pValue;
uint8_t input = 0;
uint32_t tempInt = 0;
int j = 0, i = 0, v = 0;
char buf[32];
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr) return -EINVAL;
/*getting the first argument ie measurement token*/
v = sscanf(inPtr, "%31s ", buf);
if (1 != v) return -EINVAL;
v = kstrtos8(buf, 10, &input);
if ( v < 0) return -EINVAL;
input = VOS_MIN(input, SIR_ESE_MAX_MEAS_IE_REQS);
pEseBcnReq->numBcnReqIe = input;
hddLog(LOG1, "Number of Bcn Req Ie fields: %d", pEseBcnReq->numBcnReqIe);
for (j = 0; j < (pEseBcnReq->numBcnReqIe); j++)
{
for (i = 0; i < 4; i++)
{
/*inPtr pointing to the beginning of first space after number of ie fields*/
inPtr = strpbrk( inPtr, " " );
/*no ie data after the number of ie fields argument*/
if (NULL == inPtr) return -EINVAL;
/*removing empty space*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no ie data after the number of ie fields argument and spaces*/
if ( '\0' == *inPtr ) return -EINVAL;
v = sscanf(inPtr, "%31s ", buf);
if (1 != v) return -EINVAL;
v = kstrtou32(buf, 10, &tempInt);
if (v < 0) return -EINVAL;
switch (i)
{
case 0: /* Measurement token */
if (!tempInt)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid Measurement Token: %u", tempInt);
return -EINVAL;
}
pEseBcnReq->bcnReq[j].measurementToken = tempInt;
break;
case 1: /* Channel number */
if ((!tempInt) ||
(tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid Channel Number: %u", tempInt);
return -EINVAL;
}
pEseBcnReq->bcnReq[j].channel = tempInt;
break;
case 2: /* Scan mode */
if ((tempInt < eSIR_PASSIVE_SCAN) || (tempInt > eSIR_BEACON_TABLE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid Scan Mode(%u) Expected{0|1|2}", tempInt);
return -EINVAL;
}
pEseBcnReq->bcnReq[j].scanMode= tempInt;
break;
case 3: /* Measurement duration */
if (((!tempInt) && (pEseBcnReq->bcnReq[j].scanMode != eSIR_BEACON_TABLE)) ||
((pEseBcnReq->bcnReq[j].scanMode == eSIR_BEACON_TABLE)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid Measurement Duration: %u", tempInt);
return -EINVAL;
}
pEseBcnReq->bcnReq[j].measurementDuration = tempInt;
break;
}
}
}
for (j = 0; j < pEseBcnReq->numBcnReqIe; j++)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Index(%d) Measurement Token(%u)Channel(%u) Scan Mode(%u) Measurement Duration(%u)\n",
j,
pEseBcnReq->bcnReq[j].measurementToken,
pEseBcnReq->bcnReq[j].channel,
pEseBcnReq->bcnReq[j].scanMode,
pEseBcnReq->bcnReq[j].measurementDuration);
}
return VOS_STATUS_SUCCESS;
}
struct tsm_priv {
tAniTrafStrmMetrics tsm_metrics;
};
static void hdd_get_tsm_stats_cb(tAniTrafStrmMetrics tsm_metrics,
const tANI_U32 sta_id, void *context )
{
struct hdd_request *request;
struct tsm_priv *priv;
ENTER();
request = hdd_request_get(context);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
return;
}
priv = hdd_request_priv(request);
priv->tsm_metrics = tsm_metrics;
hdd_request_complete(request);
hdd_request_put(request);
EXIT();
}
static VOS_STATUS hdd_get_tsm_stats(hdd_adapter_t *pAdapter, const tANI_U8 tid,
tAniTrafStrmMetrics* pTsmMetrics)
{
hdd_station_ctx_t *pHddStaCtx = NULL;
eHalStatus hstatus;
VOS_STATUS vstatus = VOS_STATUS_SUCCESS;
hdd_context_t *pHddCtx = NULL;
int ret;
void *cookie;
struct hdd_request *request;
struct tsm_priv *priv;
static const struct hdd_request_params params = {
.priv_size = sizeof(*priv),
.timeout_ms = WLAN_WAIT_TIME_STATS,
};
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: pAdapter is NULL", __func__);
return VOS_STATUS_E_FAULT;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
request = hdd_request_alloc(&params);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request allocation failure"));
return VOS_STATUS_E_NOMEM;
}
cookie = hdd_request_cookie(request);
/* query tsm stats */
hstatus = sme_GetTsmStats(pHddCtx->hHal, hdd_get_tsm_stats_cb,
pHddStaCtx->conn_info.staId[ 0 ],
pHddStaCtx->conn_info.bssId,
cookie, pHddCtx->pvosContext, tid);
if (eHAL_STATUS_SUCCESS != hstatus)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Unable to retrieve statistics",
__func__);
vstatus = VOS_STATUS_E_FAULT;
}
else
{
/* request was sent -- wait for the response */
ret = hdd_request_wait_for_response(request);
if (ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"SME timeout while retrieving statistics");
vstatus = VOS_STATUS_E_TIMEOUT;
} else {
priv = hdd_request_priv(request);
*pTsmMetrics = priv->tsm_metrics;
}
}
hdd_request_put(request);
return vstatus;
}
#endif /*FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
void hdd_getBand_helper(hdd_context_t *pHddCtx, int *pBand)
{
eCsrBand band = -1;
sme_GetFreqBand((tHalHandle)(pHddCtx->hHal), &band);
switch (band)
{
case eCSR_BAND_ALL:
*pBand = WLAN_HDD_UI_BAND_AUTO;
break;
case eCSR_BAND_24:
*pBand = WLAN_HDD_UI_BAND_2_4_GHZ;
break;
case eCSR_BAND_5G:
*pBand = WLAN_HDD_UI_BAND_5_GHZ;
break;
default:
hddLog( VOS_TRACE_LEVEL_WARN, "%s: Invalid Band %d", __func__, band);
*pBand = -1;
break;
}
}
/**---------------------------------------------------------------------------
\brief hdd_parse_send_action_frame_data() - HDD Parse send action frame data
This function parses the send action frame data passed in the format
SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
\param - pValue Pointer to input data
\param - pTargetApBssid Pointer to target Ap bssid
\param - pChannel Pointer to the Target AP channel
\param - pDwellTime Pointer to the time to stay off-channel after transmitting action frame
\param - pBuf Pointer to data
\param - pBufLen Pointer to data length
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_parse_send_action_frame_data(tANI_U8 *pValue, tANI_U8 *pTargetApBssid, tANI_U8 *pChannel,
tANI_U8 *pDwellTime, tANI_U8 **pBuf, tANI_U8 *pBufLen)
{
tANI_U8 *inPtr = pValue;
tANI_U8 *dataEnd;
int tempInt;
int j = 0;
int i = 0;
int v = 0;
tANI_U8 tempBuf[32];
tANI_U8 tempByte = 0;
/* 12 hexa decimal digits, 5 ':' and '\0' */
tANI_U8 macAddress[18];
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
v = sscanf(inPtr, "%17s", macAddress);
if (!((1 == v) && hdd_is_valid_mac_address(macAddress)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid MAC address or All hex inputs are not read (%d)", v);
return -EINVAL;
}
pTargetApBssid[0] = hdd_parse_hex(macAddress[0]) << 4 | hdd_parse_hex(macAddress[1]);
pTargetApBssid[1] = hdd_parse_hex(macAddress[3]) << 4 | hdd_parse_hex(macAddress[4]);
pTargetApBssid[2] = hdd_parse_hex(macAddress[6]) << 4 | hdd_parse_hex(macAddress[7]);
pTargetApBssid[3] = hdd_parse_hex(macAddress[9]) << 4 | hdd_parse_hex(macAddress[10]);
pTargetApBssid[4] = hdd_parse_hex(macAddress[12]) << 4 | hdd_parse_hex(macAddress[13]);
pTargetApBssid[5] = hdd_parse_hex(macAddress[15]) << 4 | hdd_parse_hex(macAddress[16]);
/* point to the next argument */
inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr) return -EINVAL;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/*getting the next argument ie the channel number */
v = sscanf(inPtr, "%31s ", tempBuf);
if (1 != v) return -EINVAL;
v = kstrtos32(tempBuf, 10, &tempInt);
if ( v < 0 || tempInt <= 0 || tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX )
return -EINVAL;
*pChannel = tempInt;
/* point to the next argument */
inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr) return -EINVAL;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/*getting the next argument ie the dwell time */
v = sscanf(inPtr, "%31s ", tempBuf);
if (1 != v) return -EINVAL;
v = kstrtos32(tempBuf, 10, &tempInt);
if ( v < 0 || tempInt < 0) return -EINVAL;
*pDwellTime = tempInt;
/* point to the next argument */
inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr) return -EINVAL;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/* find the length of data */
dataEnd = inPtr;
while(('\0' != *dataEnd) )
{
dataEnd++;
}
*pBufLen = dataEnd - inPtr ;
if ( *pBufLen <= 0) return -EINVAL;
/* Allocate the number of bytes based on the number of input characters
whether it is even or odd.
if the number of input characters are even, then we need N/2 byte.
if the number of input characters are odd, then we need do (N+1)/2 to
compensate rounding off.
For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes */
*pBuf = vos_mem_malloc((*pBufLen + 1)/2);
if (NULL == *pBuf)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: vos_mem_alloc failed ", __func__);
return -EINVAL;
}
/* the buffer received from the upper layer is character buffer,
we need to prepare the buffer taking 2 characters in to a U8 hex decimal number
for example 7f0000f0...form a buffer to contain 7f in 0th location, 00 in 1st
and f0 in 3rd location */
for (i = 0, j = 0; j < *pBufLen; j += 2)
{
if( j+1 == *pBufLen)
{
tempByte = hdd_parse_hex(inPtr[j]);
}
else
{
tempByte = (hdd_parse_hex(inPtr[j]) << 4) | (hdd_parse_hex(inPtr[j + 1]));
}
(*pBuf)[i++] = tempByte;
}
*pBufLen = i;
return VOS_STATUS_SUCCESS;
}
/**---------------------------------------------------------------------------
\brief hdd_parse_channellist() - HDD Parse channel list
This function parses the channel list passed in the format
SETROAMSCANCHANNELS<space><Number of channels><space>Channel 1<space>Channel 2<space>Channel N
if the Number of channels (N) does not match with the actual number of channels passed
then take the minimum of N and count of (Ch1, Ch2, ...Ch M)
For example, if SETROAMSCANCHANNELS 3 36 40 44 48, only 36, 40 and 44 shall be taken.
If SETROAMSCANCHANNELS 5 36 40 44 48, ignore 5 and take 36, 40, 44 and 48.
This function does not take care of removing duplicate channels from the list
\param - pValue Pointer to input channel list
\param - ChannelList Pointer to local output array to record channel list
\param - pNumChannels Pointer to number of roam scan channels
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_parse_channellist(tANI_U8 *pValue, tANI_U8 *pChannelList, tANI_U8 *pNumChannels)
{
tANI_U8 *inPtr = pValue;
int tempInt;
int j = 0;
int v = 0;
char buf[32];
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/*getting the first argument ie the number of channels*/
v = sscanf(inPtr, "%31s ", buf);
if (1 != v) return -EINVAL;
v = kstrtos32(buf, 10, &tempInt);
if ((v < 0) ||
(tempInt <= 0) ||
(tempInt > WNI_CFG_VALID_CHANNEL_LIST_LEN))
{
return -EINVAL;
}
*pNumChannels = tempInt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"Number of channels are: %d", *pNumChannels);
for (j = 0; j < (*pNumChannels); j++)
{
/*inPtr pointing to the beginning of first space after number of channels*/
inPtr = strpbrk( inPtr, " " );
/*no channel list after the number of channels argument*/
if (NULL == inPtr)
{
if (0 != j)
{
*pNumChannels = j;
return VOS_STATUS_SUCCESS;
}
else
{
return -EINVAL;
}
}
/*removing empty space*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
/*no channel list after the number of channels argument and spaces*/
if ( '\0' == *inPtr )
{
if (0 != j)
{
*pNumChannels = j;
return VOS_STATUS_SUCCESS;
}
else
{
return -EINVAL;
}
}
v = sscanf(inPtr, "%31s ", buf);
if (1 != v) return -EINVAL;
v = kstrtos32(buf, 10, &tempInt);
if ((v < 0) ||
(tempInt <= 0) ||
(tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX))
{
return -EINVAL;
}
pChannelList[j] = tempInt;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"Channel %d added to preferred channel list",
pChannelList[j] );
}
return VOS_STATUS_SUCCESS;
}
/**
* hdd_parse_reassoc_command_v1_data() - HDD Parse reassoc command data
* This function parses the reasoc command data passed in the format
* REASSOC<space><bssid><space><channel>
*
* @pValue: Pointer to input data (its a NUL terminated string)
* @pTargetApBssid: Pointer to target Ap bssid
* @pChannel: Pointer to the Target AP channel
*
* Return: 0 for success non-zero for failure
*/
static int hdd_parse_reassoc_command_v1_data(const tANI_U8 *pValue,
tANI_U8 *pTargetApBssid, tANI_U8 *pChannel)
{
const tANI_U8 *inPtr = pValue;
int tempInt;
int v = 0;
tANI_U8 tempBuf[32];
/* 12 hexa decimal digits, 5 ':' and '\0' */
tANI_U8 macAddress[18];
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
v = sscanf(inPtr, "%17s", macAddress);
if (!((1 == v) && hdd_is_valid_mac_address(macAddress)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid MAC address or All hex inputs are not read (%d)", v);
return -EINVAL;
}
pTargetApBssid[0] = hdd_parse_hex(macAddress[0]) << 4 | hdd_parse_hex(macAddress[1]);
pTargetApBssid[1] = hdd_parse_hex(macAddress[3]) << 4 | hdd_parse_hex(macAddress[4]);
pTargetApBssid[2] = hdd_parse_hex(macAddress[6]) << 4 | hdd_parse_hex(macAddress[7]);
pTargetApBssid[3] = hdd_parse_hex(macAddress[9]) << 4 | hdd_parse_hex(macAddress[10]);
pTargetApBssid[4] = hdd_parse_hex(macAddress[12]) << 4 | hdd_parse_hex(macAddress[13]);
pTargetApBssid[5] = hdd_parse_hex(macAddress[15]) << 4 | hdd_parse_hex(macAddress[16]);
/* point to the next argument */
inPtr = strnchr(inPtr, strlen(inPtr), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr) return -EINVAL;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/*getting the next argument ie the channel number */
v = sscanf(inPtr, "%31s ", tempBuf);
if (1 != v) return -EINVAL;
v = kstrtos32(tempBuf, 10, &tempInt);
if ((v < 0) ||
(tempInt < 0) ||
(tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX))
{
return -EINVAL;
}
*pChannel = tempInt;
return VOS_STATUS_SUCCESS;
}
#endif
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
/**---------------------------------------------------------------------------
\brief hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
This function parses the SETCCKM IE command
SETCCKMIE<space><ie data>
\param - pValue Pointer to input data
\param - pCckmIe Pointer to output cckm Ie
\param - pCckmIeLen Pointer to output cckm ie length
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_parse_get_cckm_ie(tANI_U8 *pValue, tANI_U8 **pCckmIe,
tANI_U8 *pCckmIeLen)
{
tANI_U8 *inPtr = pValue;
tANI_U8 *dataEnd;
int j = 0;
int i = 0;
tANI_U8 tempByte = 0;
inPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
/*no argument after the command*/
if (NULL == inPtr)
{
return -EINVAL;
}
/*no space after the command*/
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
/*no argument followed by spaces*/
if ('\0' == *inPtr)
{
return -EINVAL;
}
/* find the length of data */
dataEnd = inPtr;
while(('\0' != *dataEnd) )
{
dataEnd++;
++(*pCckmIeLen);
}
if ( *pCckmIeLen <= 0) return -EINVAL;
/* Allocate the number of bytes based on the number of input characters
whether it is even or odd.
if the number of input characters are even, then we need N/2 byte.
if the number of input characters are odd, then we need do (N+1)/2 to
compensate rounding off.
For example, if N = 18, then (18 + 1)/2 = 9 bytes are enough.
If N = 19, then we need 10 bytes, hence (19 + 1)/2 = 10 bytes */
*pCckmIe = vos_mem_malloc((*pCckmIeLen + 1)/2);
if (NULL == *pCckmIe)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: vos_mem_alloc failed ", __func__);
return -EINVAL;
}
vos_mem_zero(*pCckmIe, (*pCckmIeLen + 1)/2);
/* the buffer received from the upper layer is character buffer,
we need to prepare the buffer taking 2 characters in to a U8 hex decimal number
for example 7f0000f0...form a buffer to contain 7f in 0th location, 00 in 1st
and f0 in 3rd location */
for (i = 0, j = 0; j < *pCckmIeLen; j += 2)
{
tempByte = (hdd_parse_hex(inPtr[j]) << 4) | (hdd_parse_hex(inPtr[j + 1]));
(*pCckmIe)[i++] = tempByte;
}
*pCckmIeLen = i;
return VOS_STATUS_SUCCESS;
}
#endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */
/**---------------------------------------------------------------------------
\brief hdd_is_valid_mac_address() - Validate MAC address
This function validates whether the given MAC address is valid or not
Expected MAC address is of the format XX:XX:XX:XX:XX:XX
where X is the hexa decimal digit character and separated by ':'
This algorithm works even if MAC address is not separated by ':'
This code checks given input string mac contains exactly 12 hexadecimal digits.
and a separator colon : appears in the input string only after
an even number of hex digits.
\param - pMacAddr pointer to the input MAC address
\return - 1 for valid and 0 for invalid
--------------------------------------------------------------------------*/
v_BOOL_t hdd_is_valid_mac_address(const tANI_U8 *pMacAddr)
{
int xdigit = 0;
int separator = 0;
while (*pMacAddr)
{
if (isxdigit(*pMacAddr))
{
xdigit++;
}
else if (':' == *pMacAddr)
{
if (0 == xdigit || ((xdigit / 2) - 1) != separator)
break;
++separator;
}
else
{
separator = -1;
/* Invalid MAC found */
return 0;
}
++pMacAddr;
}
return (xdigit == 12 && (separator == 5 || separator == 0));
}
/**---------------------------------------------------------------------------
\brief __hdd_open() - HDD Open function
\param - dev Pointer to net_device structure
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
int __hdd_open(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
v_BOOL_t in_standby = TRUE;
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: pAdapter is Null", __func__);
return -ENODEV;
}
pHddCtx = (hdd_context_t*)pAdapter->pHddCtx;
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
pAdapter->sessionId, pAdapter->device_mode));
if (NULL == pHddCtx)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD context is Null", __func__);
return -ENODEV;
}
if (test_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags)) {
hddLog(VOS_TRACE_LEVEL_DEBUG, "%s: session already opened for the adapter",
__func__);
return 0;
}
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( (NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status) )
{
if (test_bit(DEVICE_IFACE_OPENED, &pAdapterNode->pAdapter->event_flags))
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: chip already out of standby",
__func__);
in_standby = FALSE;
break;
}
else
{
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
if (TRUE == in_standby)
{
if (VOS_STATUS_SUCCESS != wlan_hdd_exit_lowpower(pHddCtx, pAdapter))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to bring "
"wlan out of power save", __func__);
return -EINVAL;
}
}
status = hdd_init_station_mode( pAdapter );
if( VOS_STATUS_SUCCESS != status ) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to create session for station mode",
__func__);
return -EINVAL;
}
set_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Enabling Tx Queues", __func__);
/* Enable TX queues only when we are connected */
hddLog(VOS_TRACE_LEVEL_INFO, FL("Enabling queues"));
netif_tx_start_all_queues(dev);
}
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_open() - Wrapper function for __hdd_open to protect it from SSR
This is called in response to ifconfig up
\param - dev Pointer to net_device structure
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
int hdd_open(struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_open(dev);
vos_ssr_unprotect(__func__);
return ret;
}
int __hdd_mon_open (struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_adapter_t *sta_adapter;
hdd_context_t *hdd_ctx;
if(pAdapter == NULL) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: HDD adapter context is Null", __func__);
return -EINVAL;
}
if (vos_get_concurrency_mode() != VOS_STA_MON)
return 0;
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!sta_adapter) {
hddLog(LOGE, FL("No valid STA interface"));
return -EINVAL;
}
if (!test_bit(DEVICE_IFACE_OPENED, &sta_adapter->event_flags)) {
hddLog(LOGE, FL("STA Interface is not OPENED"));
return -EINVAL;
}
set_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
return 0;
}
int hdd_mon_open (struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_mon_open(dev);
vos_ssr_unprotect(__func__);
return ret;
}
int __hdd_mon_stop (struct net_device *dev)
{
hdd_adapter_t *mon_adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx;
if (vos_get_concurrency_mode() != VOS_STA_MON)
return 0;
if(!mon_adapter) {
hddLog(LOGE, FL("HDD adapter is Null"));
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(mon_adapter);
if (wlan_hdd_validate_context(hdd_ctx))
return -EINVAL;
if (!test_bit(DEVICE_IFACE_OPENED, &mon_adapter->event_flags)) {
hddLog(LOGE, FL("NETDEV Interface is not OPENED"));
return -ENODEV;
}
clear_bit(DEVICE_IFACE_OPENED, &mon_adapter->event_flags);
hdd_stop_adapter(hdd_ctx, mon_adapter, VOS_FALSE);
return 0;
}
int hdd_mon_stop(struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_mon_stop(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**---------------------------------------------------------------------------
\brief __hdd_stop() - HDD stop function
\param - dev Pointer to net_device structure
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
int __hdd_stop (struct net_device *dev)
{
int ret = 0;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
v_BOOL_t enter_standby = TRUE;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: pAdapter is Null", __func__);
return -ENODEV;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_STOP_REQUEST,
pAdapter->sessionId, pAdapter->device_mode));
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (ret)
{
return ret;
}
/* Nothing to be done if the interface is not opened */
if (VOS_FALSE == test_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: NETDEV Interface is not OPENED", __func__);
return -ENODEV;
}
if (pHddCtx->concurrency_mode == VOS_STA_MON) {
/*
* In STA + Monitor mode concurrency, no point in running
* capture on monitor interface, when STA interface is stopped
*/
wlan_hdd_stop_mon(pHddCtx, true);
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Disabling OS Tx queues", __func__);
/* Disable TX on the interface, after this hard_start_xmit() will not
* be called on that interface
*/
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
/* Mark the interface status as "down" for outside world */
netif_carrier_off(pAdapter->dev);
/* The interface is marked as down for outside world (aka kernel)
* But the driver is pretty much alive inside. The driver needs to
* tear down the existing connection on the netdev (session)
* cleanup the data pipes and wait until the control plane is stabilized
* for this interface. The call also needs to wait until the above
* mentioned actions are completed before returning to the caller.
* Notice that the hdd_stop_adapter is requested not to close the session
* That is intentional to be able to scan if it is a STA/P2P interface
*/
hdd_stop_adapter(pHddCtx, pAdapter, VOS_TRUE);
clear_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
#endif
/* DeInit the adapter. This ensures datapath cleanup as well */
hdd_deinit_adapter(pHddCtx, pAdapter, TRUE);
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
/* SoftAP ifaces should never go in power save mode
making sure same here. */
if ( (WLAN_HDD_SOFTAP == pAdapter->device_mode )
|| (WLAN_HDD_P2P_GO == pAdapter->device_mode )
)
{
/* SoftAP mode, so return from here */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: In SAP MODE", __func__);
EXIT();
return 0;
}
/* Find if any iface is up. If any iface is up then can't put device to
* sleep/power save mode
*/
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( (NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status) )
{
if (test_bit(DEVICE_IFACE_OPENED, &pAdapterNode->pAdapter->event_flags))
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Still other ifaces are up cannot "
"put device to sleep", __func__);
enter_standby = FALSE;
break;
}
else
{
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
if (TRUE == enter_standby)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: All Interfaces are Down "
"entering standby", __func__);
if (VOS_STATUS_SUCCESS != wlan_hdd_enter_lowpower(pHddCtx))
{
/*log and return success*/
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Failed to put "
"wlan in power save", __func__);
}
}
pAdapter->dev->wireless_handlers = NULL;
/*
* Upon wifi turn off, DUT has to flush the scan results so if
* this is the last cli iface, flush the scan database.
*/
if (!hdd_is_cli_iface_up(pHddCtx))
sme_ScanFlushResult(pHddCtx->hHal, 0);
EXIT();
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_stop() - wrapper_function for __hdd_stop to protect it from SSR
This is called in response to ifconfig down
\param - dev Pointer to net_device structure
\return - 0 for success non-zero for failure
-----------------------------------------------------------------------------*/
int hdd_stop (struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_stop(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**---------------------------------------------------------------------------
\brief __hdd_uninit() - HDD uninit function
\param - dev Pointer to net_device structure
\return - void
--------------------------------------------------------------------------*/
static void __hdd_uninit (struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
ENTER();
do
{
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: NULL pAdapter", __func__);
break;
}
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Invalid magic", __func__);
break;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: NULL pHddCtx", __func__);
break;
}
if (dev != pAdapter->dev)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Invalid device reference", __func__);
/* we haven't validated all cases so let this go for now */
}
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
#endif
hdd_deinit_adapter(pHddCtx, pAdapter, TRUE);
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
/* after uninit our adapter structure will no longer be valid */
pAdapter->dev = NULL;
pAdapter->magic = 0;
pAdapter->pHddCtx = NULL;
} while (0);
EXIT();
}
/**---------------------------------------------------------------------------
\brief hdd_uninit() - Wrapper function to protect __hdd_uninit from SSR
This is called during the netdev unregister to uninitialize all data
associated with the device
\param - dev Pointer to net_device structure
\return - void
--------------------------------------------------------------------------*/
static void hdd_uninit (struct net_device *dev)
{
vos_ssr_protect(__func__);
__hdd_uninit(dev);
vos_ssr_unprotect(__func__);
}
/**---------------------------------------------------------------------------
\brief hdd_release_firmware() -
This function calls the release firmware API to free the firmware buffer.
\param - pFileName Pointer to the File Name.
pCtx - Pointer to the adapter .
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_release_firmware(char *pFileName,v_VOID_t *pCtx)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_context_t *pHddCtx = (hdd_context_t*)pCtx;
ENTER();
if (!strcmp(WLAN_FW_FILE, pFileName)) {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"%s: Loaded firmware file is %s",__func__,pFileName);
if(pHddCtx->fw) {
release_firmware(pHddCtx->fw);
pHddCtx->fw = NULL;
}
else
status = VOS_STATUS_E_FAILURE;
}
else if (!strcmp(WLAN_NV_FILE,pFileName)) {
if(pHddCtx->nv) {
release_firmware(pHddCtx->nv);
pHddCtx->nv = NULL;
}
else
status = VOS_STATUS_E_FAILURE;
}
EXIT();
return status;
}
/**---------------------------------------------------------------------------
\brief hdd_request_firmware() -
This function reads the firmware file using the request firmware
API and returns the the firmware data and the firmware file size.
\param - pfileName - Pointer to the file name.
- pCtx - Pointer to the adapter .
- ppfw_data - Pointer to the pointer of the firmware data.
- pSize - Pointer to the file size.
\return - VOS_STATUS_SUCCESS for success, VOS_STATUS_E_FAILURE for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_request_firmware(char *pfileName,v_VOID_t *pCtx,v_VOID_t **ppfw_data, v_SIZE_t *pSize)
{
int status;
VOS_STATUS retval = VOS_STATUS_SUCCESS;
hdd_context_t *pHddCtx = (hdd_context_t*)pCtx;
ENTER();
if( (!strcmp(WLAN_FW_FILE, pfileName)) ) {
status = request_firmware(&pHddCtx->fw, pfileName, pHddCtx->parent_dev);
if(status || !pHddCtx->fw || !pHddCtx->fw->data) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Firmware %s download failed",
__func__, pfileName);
retval = VOS_STATUS_E_FAILURE;
}
else {
*ppfw_data = (v_VOID_t *)pHddCtx->fw->data;
*pSize = pHddCtx->fw->size;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Firmware size = %d",
__func__, *pSize);
}
}
else if(!strcmp(WLAN_NV_FILE, pfileName)) {
status = request_firmware(&pHddCtx->nv, pfileName, pHddCtx->parent_dev);
if(status || !pHddCtx->nv || !pHddCtx->nv->data) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: nv %s download failed",
__func__, pfileName);
retval = VOS_STATUS_E_FAILURE;
}
else {
*ppfw_data = (v_VOID_t *)pHddCtx->nv->data;
*pSize = pHddCtx->nv->size;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: nv file size = %d",
__func__, *pSize);
}
}
EXIT();
return retval;
}
/**---------------------------------------------------------------------------
\brief hdd_full_pwr_cbk() - HDD full power callbackfunction
This is the function invoked by SME to inform the result of a full power
request issued by HDD
\param - callbackcontext - Pointer to cookie
status - result of request
\return - None
--------------------------------------------------------------------------*/
void hdd_full_pwr_cbk(void *callbackContext, eHalStatus status)
{
hdd_context_t *pHddCtx = (hdd_context_t*)callbackContext;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,"HDD full Power callback status = %d", status);
if(&pHddCtx->full_pwr_comp_var)
{
complete(&pHddCtx->full_pwr_comp_var);
}
}
#ifdef WLAN_FEATURE_RMC
static void hdd_tx_fail_ind_callback(v_U8_t *MacAddr, v_U8_t seqNo)
{
int payload_len;
struct sk_buff *skb;
struct nlmsghdr *nlh;
v_U8_t *data;
payload_len = ETH_ALEN;
if (0 == cesium_pid)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: cesium process not registered",
__func__);
return;
}
if ((skb = nlmsg_new(payload_len,GFP_ATOMIC)) == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: nlmsg_new() failed for msg size[%d]",
__func__, NLMSG_SPACE(payload_len));
return;
}
nlh = nlmsg_put(skb, cesium_pid, seqNo, 0, payload_len, NLM_F_REQUEST);
if (NULL == nlh)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: nlmsg_put() failed for msg size[%d]",
__func__, NLMSG_SPACE(payload_len));
kfree_skb(skb);
return;
}
data = nlmsg_data(nlh);
memcpy(data, MacAddr, ETH_ALEN);
if (nlmsg_unicast(cesium_nl_srv_sock, skb, cesium_pid) < 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: nlmsg_unicast() failed for msg size[%d]",
__func__, NLMSG_SPACE(payload_len));
}
return;
}
/**---------------------------------------------------------------------------
\brief hdd_ParseuserParams - return a pointer to the next argument
\return - status
--------------------------------------------------------------------------*/
static int hdd_ParseUserParams(tANI_U8 *pValue, tANI_U8 **ppArg)
{
tANI_U8 *pVal;
pVal = strchr(pValue, ' ');
if (NULL == pVal)
{
/* no argument remains */
return -EINVAL;
}
else if (SPACE_ASCII_VALUE != *pVal)
{
/* no space after the current argument */
return -EINVAL;
}
pVal++;
/* remove empty spaces */
while ((SPACE_ASCII_VALUE == *pVal) && ('\0' != *pVal))
{
pVal++;
}
/* no argument followed by spaces */
if ('\0' == *pVal)
{
return -EINVAL;
}
*ppArg = pVal;
return 0;
}
/**----------------------------------------------------------------------------
\brief hdd_ParseIBSSTXFailEventParams - Parse params for SETIBSSTXFAILEVENT
\return - status
------------------------------------------------------------------------------*/
static int hdd_ParseIBSSTXFailEventParams(tANI_U8 *pValue,
tANI_U8 *tx_fail_count,
tANI_U16 *pid)
{
tANI_U8 *param = NULL;
int ret;
ret = hdd_ParseUserParams(pValue, &param);
if (0 == ret && NULL != param)
{
if (1 != sscanf(param, "%hhu", tx_fail_count))
{
ret = -EINVAL;
goto done;
}
}
else
{
goto done;
}
if (0 == *tx_fail_count)
{
*pid = 0;
goto done;
}
pValue = param;
pValue++;
ret = hdd_ParseUserParams(pValue, &param);
if (0 == ret)
{
if (1 != sscanf(param, "%hu", pid))
{
ret = -EINVAL;
goto done;
}
}
else
{
goto done;
}
done:
return ret;
}
static int hdd_open_cesium_nl_sock()
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct netlink_kernel_cfg cfg = {
.groups = WLAN_NLINK_MCAST_GRP_ID,
.input = NULL
};
#endif
int ret = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0))
THIS_MODULE,
#endif
&cfg);
#else
cesium_nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_CESIUM,
WLAN_NLINK_MCAST_GRP_ID, NULL, NULL, THIS_MODULE);
#endif
if (cesium_nl_srv_sock == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"NLINK: cesium netlink_kernel_create failed");
ret = -ECONNREFUSED;
}
return ret;
}
static void hdd_close_cesium_nl_sock()
{
if (NULL != cesium_nl_srv_sock)
{
netlink_kernel_release(cesium_nl_srv_sock);
cesium_nl_srv_sock = NULL;
}
}
#endif /* WLAN_FEATURE_RMC */
/**---------------------------------------------------------------------------
\brief hdd_req_bmps_cbk() - HDD Request BMPS callback function
This is the function invoked by SME to inform the result of BMPS
request issued by HDD
\param - callbackcontext - Pointer to cookie
status - result of request
\return - None
--------------------------------------------------------------------------*/
void hdd_req_bmps_cbk(void *callbackContext, eHalStatus status)
{
struct completion *completion_var = (struct completion*) callbackContext;
hddLog(VOS_TRACE_LEVEL_ERROR, "HDD BMPS request Callback, status = %d", status);
if(completion_var != NULL)
{
complete(completion_var);
}
}
/**---------------------------------------------------------------------------
\brief hdd_get_cfg_file_size() -
This function reads the configuration file using the request firmware
API and returns the configuration file size.
\param - pCtx - Pointer to the adapter .
- pFileName - Pointer to the file name.
- pBufSize - Pointer to the buffer size.
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_get_cfg_file_size(v_VOID_t *pCtx, char *pFileName, v_SIZE_t *pBufSize)
{
int status;
hdd_context_t *pHddCtx = (hdd_context_t*)pCtx;
ENTER();
status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev);
if(status || !pHddCtx->fw || !pHddCtx->fw->data) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__);
status = VOS_STATUS_E_FAILURE;
}
else {
*pBufSize = pHddCtx->fw->size;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: CFG size = %d", __func__, *pBufSize);
release_firmware(pHddCtx->fw);
pHddCtx->fw = NULL;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
/**---------------------------------------------------------------------------
\brief hdd_read_cfg_file() -
This function reads the configuration file using the request firmware
API and returns the cfg data and the buffer size of the configuration file.
\param - pCtx - Pointer to the adapter .
- pFileName - Pointer to the file name.
- pBuffer - Pointer to the data buffer.
- pBufSize - Pointer to the buffer size.
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
VOS_STATUS hdd_read_cfg_file(v_VOID_t *pCtx, char *pFileName,
v_VOID_t *pBuffer, v_SIZE_t *pBufSize)
{
int status;
hdd_context_t *pHddCtx = (hdd_context_t*)pCtx;
ENTER();
status = request_firmware(&pHddCtx->fw, pFileName, pHddCtx->parent_dev);
if(status || !pHddCtx->fw || !pHddCtx->fw->data) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: CFG download failed",__func__);
return VOS_STATUS_E_FAILURE;
}
else {
if(*pBufSize != pHddCtx->fw->size) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Caller sets invalid CFG "
"file size", __func__);
release_firmware(pHddCtx->fw);
pHddCtx->fw = NULL;
return VOS_STATUS_E_FAILURE;
}
else {
if(pBuffer) {
vos_mem_copy(pBuffer,pHddCtx->fw->data,*pBufSize);
}
release_firmware(pHddCtx->fw);
pHddCtx->fw = NULL;
}
}
EXIT();
return VOS_STATUS_SUCCESS;
}
/**---------------------------------------------------------------------------
\brief __hdd_set_mac_address() -
This function sets the user specified mac address using
the command ifconfig wlanX hw ether <mac adress>.
\param - dev - Pointer to the net device.
- addr - Pointer to the sockaddr.
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
static int __hdd_set_mac_address(struct net_device *dev, void *addr)
{
hdd_adapter_t *pAdapter;
hdd_adapter_t *adapter_temp;
hdd_context_t *pHddCtx;
struct sockaddr *psta_mac_addr = addr;
int ret = 0, i;
v_MACADDR_t mac_addr;
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);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return ret;
memcpy(&mac_addr, psta_mac_addr->sa_data, sizeof(mac_addr));
if(vos_is_macaddr_zero(&mac_addr)) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Zero Mac address");
return -EINVAL;
}
if (vos_is_macaddr_broadcast(&mac_addr)) {
hddLog(VOS_TRACE_LEVEL_ERROR,"MAC is Broadcast");
return -EINVAL;
}
if (vos_is_macaddr_multicast(&mac_addr)) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Multicast Mac address");
return -EINVAL;
}
adapter_temp = hdd_get_adapter_by_macaddr(pHddCtx, mac_addr.bytes);
if (adapter_temp) {
if (!strcmp(adapter_temp->dev->name, dev->name))
return 0;
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WLAN Mac Addr: "
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(mac_addr.bytes));
return -EINVAL;
}
for (i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++) {
if (!vos_mem_compare(&pAdapter->macAddressCurrent.bytes,
&pHddCtx->cfg_ini->intfMacAddr[i].bytes[0], VOS_MAC_ADDR_SIZE)) {
memcpy(&pHddCtx->cfg_ini->intfMacAddr[i].bytes[0], mac_addr.bytes,
VOS_MAC_ADDR_SIZE);
break;
}
}
memcpy(&pAdapter->macAddressCurrent, psta_mac_addr->sa_data, ETH_ALEN);
memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN);
EXIT();
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_set_mac_address() -
Wrapper function to protect __hdd_set_mac_address() function from ssr
\param - dev - Pointer to the net device.
- addr - Pointer to the sockaddr.
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
static int hdd_set_mac_address(struct net_device *dev, void *addr)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_set_mac_address(dev, addr);
vos_ssr_unprotect(__func__);
return ret;
}
tANI_U8* wlan_hdd_get_intf_addr(hdd_context_t* pHddCtx)
{
int i;
for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++)
{
if( 0 == ((pHddCtx->cfg_ini->intfAddrMask) & (1 << i)) )
break;
}
if( VOS_MAX_CONCURRENCY_PERSONA == i)
return NULL;
pHddCtx->cfg_ini->intfAddrMask |= (1 << i);
return &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0];
}
void wlan_hdd_release_intf_addr(hdd_context_t* pHddCtx, tANI_U8* releaseAddr)
{
int i;
for ( i = 0; i < VOS_MAX_CONCURRENCY_PERSONA; i++)
{
if ( !memcmp(releaseAddr, &pHddCtx->cfg_ini->intfMacAddr[i].bytes[0], 6) )
{
pHddCtx->cfg_ini->intfAddrMask &= ~(1 << i);
break;
}
}
return;
}
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
static struct net_device_ops wlan_drv_ops = {
.ndo_open = hdd_open,
.ndo_stop = hdd_stop,
.ndo_uninit = hdd_uninit,
.ndo_start_xmit = hdd_hard_start_xmit,
.ndo_tx_timeout = hdd_tx_timeout,
.ndo_get_stats = hdd_stats,
.ndo_do_ioctl = hdd_ioctl,
.ndo_set_mac_address = hdd_set_mac_address,
.ndo_select_queue = hdd_select_queue,
#ifdef WLAN_FEATURE_PACKET_FILTERING
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0))
.ndo_set_rx_mode = hdd_set_multicast_list,
#else
.ndo_set_multicast_list = hdd_set_multicast_list,
#endif //LINUX_VERSION_CODE
#endif
};
static struct net_device_ops wlan_mon_drv_ops = {
.ndo_open = hdd_mon_open,
.ndo_stop = hdd_mon_stop,
.ndo_uninit = hdd_uninit,
.ndo_start_xmit = hdd_mon_hard_start_xmit,
.ndo_tx_timeout = hdd_tx_timeout,
.ndo_get_stats = hdd_stats,
.ndo_do_ioctl = hdd_mon_ioctl,
.ndo_set_mac_address = hdd_set_mac_address,
};
#endif
void hdd_set_station_ops( struct net_device *pWlanDev )
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
pWlanDev->netdev_ops = &wlan_drv_ops;
#else
pWlanDev->open = hdd_open;
pWlanDev->stop = hdd_stop;
pWlanDev->uninit = hdd_uninit;
pWlanDev->hard_start_xmit = NULL;
pWlanDev->tx_timeout = hdd_tx_timeout;
pWlanDev->get_stats = hdd_stats;
pWlanDev->do_ioctl = hdd_ioctl;
pWlanDev->set_mac_address = hdd_set_mac_address;
#endif
}
void hdd_set_ibss_ops( hdd_adapter_t *pAdapter )
{
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
wlan_drv_ops.ndo_start_xmit = hdd_ibss_hard_start_xmit;
#else
pAdapter->dev->hard_start_xmit = hdd_ibss_hard_start_xmit;
#endif
}
static hdd_adapter_t* hdd_alloc_station_adapter( hdd_context_t *pHddCtx, tSirMacAddr macAddr, const char* name )
{
struct net_device *pWlanDev = NULL;
hdd_adapter_t *pAdapter = NULL;
/*
* cfg80211 initialization and registration....
*/
pWlanDev = alloc_netdev_mq(sizeof( hdd_adapter_t ), name,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0))
NET_NAME_UNKNOWN,
#endif
ether_setup, NUM_TX_QUEUES);
if(pWlanDev != NULL)
{
//Save the pointer to the net_device in the HDD adapter
pAdapter = (hdd_adapter_t*) netdev_priv( pWlanDev );
vos_mem_zero( pAdapter, sizeof( hdd_adapter_t ) );
pAdapter->dev = pWlanDev;
pAdapter->pHddCtx = pHddCtx;
pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC;
spin_lock_init(&pAdapter->lock_for_active_session);
#ifdef FEATURE_WLAN_BATCH_SCAN
pAdapter->pBatchScanRsp = NULL;
pAdapter->numScanList = 0;
pAdapter->batchScanState = eHDD_BATCH_SCAN_STATE_STOPPED;
pAdapter->prev_batch_id = 0;
mutex_init(&pAdapter->hdd_batch_scan_lock);
#endif
pAdapter->isLinkUpSvcNeeded = FALSE;
pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE;
//Init the net_device structure
strlcpy(pWlanDev->name, name, IFNAMSIZ);
vos_mem_copy(pWlanDev->dev_addr, (void *)macAddr, sizeof(tSirMacAddr));
vos_mem_copy( pAdapter->macAddressCurrent.bytes, macAddr, sizeof(tSirMacAddr));
pWlanDev->watchdog_timeo = HDD_TX_TIMEOUT;
pWlanDev->needed_headroom = LIBRA_HW_NEEDED_HEADROOM;
hdd_set_station_ops( pAdapter->dev );
pWlanDev->destructor = free_netdev;
pWlanDev->ieee80211_ptr = &pAdapter->wdev ;
pAdapter->wdev.wiphy = pHddCtx->wiphy;
pAdapter->wdev.netdev = pWlanDev;
/* set pWlanDev's parent to underlying device */
SET_NETDEV_DEV(pWlanDev, pHddCtx->parent_dev);
hdd_wmm_init( pAdapter );
}
return pAdapter;
}
VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held )
{
struct net_device *pWlanDev = pAdapter->dev;
//hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
//hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
//eHalStatus halStatus = eHAL_STATUS_SUCCESS;
if( rtnl_lock_held )
{
if (strnchr(pWlanDev->name, strlen(pWlanDev->name), '%')) {
if( dev_alloc_name(pWlanDev, pWlanDev->name) < 0 )
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:dev_alloc_name",__func__);
return VOS_STATUS_E_FAILURE;
}
}
if (register_netdevice(pWlanDev))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s:Failed:register_netdev",__func__);
return VOS_STATUS_E_FAILURE;
}
}
else
{
if(register_netdev(pWlanDev))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed:register_netdev",__func__);
return VOS_STATUS_E_FAILURE;
}
}
set_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags);
return VOS_STATUS_SUCCESS;
}
static eHalStatus hdd_smeCloseSessionCallback(void *pContext)
{
hdd_adapter_t *pAdapter = pContext;
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: NULL pAdapter", __func__);
return eHAL_STATUS_INVALID_PARAMETER;
}
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Invalid magic", __func__);
return eHAL_STATUS_NOT_INITIALIZED;
}
clear_bit(SME_SESSION_OPENED, &pAdapter->event_flags);
#ifndef WLAN_OPEN_SOURCE
/* need to make sure all of our scheduled work has completed.
* This callback is called from MC thread context, so it is safe to
* to call below flush workqueue API from here.
*
* Even though this is called from MC thread context, if there is a faulty
* work item in the system, that can hang this call forever. So flushing
* this global work queue is not safe; and now we make sure that
* individual work queues are stopped correctly. But the cancel work queue
* is a GPL only API, so the proprietary version of the driver would still
* rely on the global work queue flush.
*/
flush_scheduled_work();
#endif
/* We can be blocked while waiting for scheduled work to be
* flushed, and the adapter structure can potentially be freed, in
* which case the magic will have been reset. So make sure the
* magic is still good, and hence the adapter structure is still
* valid, before signaling completion */
if (WLAN_HDD_ADAPTER_MAGIC == pAdapter->magic)
{
complete(&pAdapter->session_close_comp_var);
}
return eHAL_STATUS_SUCCESS;
}
/**
* hdd_close_tx_queues() - close tx queues
* @hdd_ctx: hdd global context
*
* Return: None
*/
static void hdd_close_tx_queues(hdd_context_t *hdd_ctx)
{
VOS_STATUS status;
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *adapter_node = NULL, *next_adapter = NULL;
/* Not validating hdd_ctx as it's already done by the caller */
ENTER();
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if (adapter && adapter->dev) {
netif_tx_disable (adapter->dev);
netif_carrier_off(adapter->dev);
}
status = hdd_get_next_adapter(hdd_ctx, adapter_node,
&next_adapter);
adapter_node = next_adapter;
}
EXIT();
}
VOS_STATUS hdd_init_station_mode( hdd_adapter_t *pAdapter )
{
struct net_device *pWlanDev = pAdapter->dev;
hdd_station_ctx_t *pHddStaCtx = &pAdapter->sessionCtx.station;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
eHalStatus halStatus = eHAL_STATUS_SUCCESS;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
long rc = 0;
spin_lock_init( &pAdapter->sta_hash_lock);
pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
INIT_COMPLETION(pAdapter->session_open_comp_var);
sme_SetCurrDeviceMode(pHddCtx->hHal, pAdapter->device_mode);
//Open a SME session for future operation
halStatus = sme_OpenSession( pHddCtx->hHal, hdd_smeRoamCallback, pAdapter,
(tANI_U8 *)&pAdapter->macAddressCurrent, &pAdapter->sessionId);
if ( !HAL_STATUS_SUCCESS( halStatus ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"sme_OpenSession() failed with status code %08d [x%08x]",
halStatus, halStatus );
status = VOS_STATUS_E_FAILURE;
goto error_sme_open;
}
//Block on a completion variable. Can't wait forever though.
rc = wait_for_completion_timeout(
&pAdapter->session_open_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE));
if (rc <= 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"Session is not opened within timeout period code %ld", rc );
status = VOS_STATUS_E_FAILURE;
goto error_sme_open;
}
// Register wireless extensions
if( eHAL_STATUS_SUCCESS != (halStatus = hdd_register_wext(pWlanDev)))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"hdd_register_wext() failed with status code %08d [x%08x]",
halStatus, halStatus );
status = VOS_STATUS_E_FAILURE;
goto error_register_wext;
}
//Safe to register the hard_start_xmit function again
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29))
wlan_drv_ops.ndo_start_xmit = hdd_hard_start_xmit;
#else
pWlanDev->hard_start_xmit = hdd_hard_start_xmit;
#endif
//Set the Connection State to Not Connected
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Set HDD connState to eConnectionState_NotConnected",
__func__);
pHddStaCtx->conn_info.connState = eConnectionState_NotConnected;
//Set the default operation channel
pHddStaCtx->conn_info.operationChannel = pHddCtx->cfg_ini->OperatingChannel;
/* Make the default Auth Type as OPEN*/
pHddStaCtx->conn_info.authType = eCSR_AUTH_TYPE_OPEN_SYSTEM;
if( VOS_STATUS_SUCCESS != ( status = hdd_init_tx_rx( pAdapter ) ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"hdd_init_tx_rx() failed with status code %08d [x%08x]",
status, status );
goto error_init_txrx;
}
set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
if( VOS_STATUS_SUCCESS != ( status = hdd_wmm_adapter_init( pAdapter ) ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"hdd_wmm_adapter_init() failed with status code %08d [x%08x]",
status, status );
goto error_wmm_init;
}
set_bit(WMM_INIT_DONE, &pAdapter->event_flags);
return VOS_STATUS_SUCCESS;
error_wmm_init:
clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
hdd_deinit_tx_rx(pAdapter);
error_init_txrx:
hdd_UnregisterWext(pWlanDev);
error_register_wext:
if (test_bit(SME_SESSION_OPENED, &pAdapter->event_flags))
{
INIT_COMPLETION(pAdapter->session_close_comp_var);
if (eHAL_STATUS_SUCCESS == sme_CloseSession(pHddCtx->hHal,
pAdapter->sessionId, FALSE, VOS_TRUE,
hdd_smeCloseSessionCallback, pAdapter))
{
unsigned long rc;
//Block on a completion variable. Can't wait forever though.
rc = wait_for_completion_timeout(
&pAdapter->session_close_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE));
if (rc <= 0)
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Session is not opened within timeout period code %ld"), rc);
}
}
error_sme_open:
return status;
}
void hdd_cleanup_actionframe( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter )
{
hdd_cfg80211_state_t *cfgState;
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
if( NULL != cfgState->buf )
{
long rc;
INIT_COMPLETION(pAdapter->tx_action_cnf_event);
rc = wait_for_completion_interruptible_timeout(
&pAdapter->tx_action_cnf_event,
msecs_to_jiffies(ACTION_FRAME_TX_TIMEOUT));
if (rc <= 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s ERROR: HDD Wait for Action Confirmation Failed!! %ld"
, __func__, rc);
// Inform tx status as FAILURE to upper layer and free cfgState->buf
hdd_sendActionCnf( pAdapter, FALSE );
}
}
return;
}
void hdd_deinit_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, tANI_U8 rtnl_held )
{
ENTER();
switch ( pAdapter->device_mode )
{
case WLAN_HDD_IBSS:
{
if(test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags))
{
hdd_ibss_deinit_tx_rx( pAdapter );
clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
}
}
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
{
if(test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags))
{
hdd_deinit_tx_rx( pAdapter );
clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
}
if(test_bit(WMM_INIT_DONE, &pAdapter->event_flags))
{
hdd_wmm_adapter_close( pAdapter );
clear_bit(WMM_INIT_DONE, &pAdapter->event_flags);
}
hdd_cleanup_actionframe(pHddCtx, pAdapter);
break;
}
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_GO:
{
if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags))
{
hdd_wmm_adapter_close( pAdapter );
clear_bit(WMM_INIT_DONE, &pAdapter->event_flags);
}
hdd_cleanup_actionframe(pHddCtx, pAdapter);
hdd_unregister_hostapd(pAdapter, rtnl_held);
/* 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);
break;
}
case WLAN_HDD_MONITOR:
{
if(test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags))
{
hdd_deinit_tx_rx( pAdapter );
clear_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
}
break;
}
default:
break;
}
EXIT();
}
void hdd_cleanup_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter, tANI_U8 rtnl_held )
{
struct net_device *pWlanDev = NULL;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return;
}
pWlanDev = pAdapter->dev;
#ifdef FEATURE_WLAN_BATCH_SCAN
if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION)
|| (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
|| (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)
)
{
if (pAdapter)
{
if (eHDD_BATCH_SCAN_STATE_STARTED == pAdapter->batchScanState)
{
hdd_deinit_batch_scan(pAdapter);
}
}
}
#endif
if(test_bit(NET_DEVICE_REGISTERED, &pAdapter->event_flags)) {
if( rtnl_held )
{
unregister_netdevice(pWlanDev);
}
else
{
unregister_netdev(pWlanDev);
}
// note that the pAdapter is no longer valid at this point
// since the memory has been reclaimed
}
EXIT();
}
void hdd_set_pwrparams(hdd_context_t *pHddCtx)
{
VOS_STATUS status;
hdd_adapter_t *pAdapter = NULL;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
/*loop through all adapters.*/
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if ( (WLAN_HDD_INFRA_STATION != pAdapter->device_mode)
&& (WLAN_HDD_P2P_CLIENT != pAdapter->device_mode) )
{ // we skip this registration for modes other than STA and P2P client modes.
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
continue;
}
//Apply Dynamic DTIM For P2P
//Only if ignoreDynamicDtimInP2pMode is not set in ini
if ((pHddCtx->cfg_ini->enableDynamicDTIM ||
pHddCtx->cfg_ini->enableModulatedDTIM) &&
((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
((WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) &&
!(pHddCtx->cfg_ini->ignoreDynamicDtimInP2pMode))) &&
(eANI_BOOLEAN_TRUE == pAdapter->higherDtimTransition) &&
(eConnectionState_Associated ==
(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState) &&
(pHddCtx->cfg_ini->fIsBmpsEnabled))
{
tSirSetPowerParamsReq powerRequest = { 0 };
powerRequest.uIgnoreDTIM = 1;
powerRequest.uMaxLIModulatedDTIM = pHddCtx->cfg_ini->fMaxLIModulatedDTIM;
if (pHddCtx->cfg_ini->enableModulatedDTIM)
{
powerRequest.uDTIMPeriod = pHddCtx->cfg_ini->enableModulatedDTIM;
powerRequest.uListenInterval = pHddCtx->hdd_actual_LI_value;
}
else
{
powerRequest.uListenInterval = pHddCtx->cfg_ini->enableDynamicDTIM;
}
/* Update ignoreDTIM and ListedInterval in CFG to remain at the DTIM
* specified during Enter/Exit BMPS when LCD off*/
ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, powerRequest.uIgnoreDTIM,
NULL, eANI_BOOLEAN_FALSE);
ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, powerRequest.uListenInterval,
NULL, eANI_BOOLEAN_FALSE);
/* switch to the DTIM specified in cfg.ini */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Switch to DTIM %d Listen interval %d",
powerRequest.uDTIMPeriod,
powerRequest.uListenInterval);
sme_SetPowerParams( pHddCtx->hHal, &powerRequest, TRUE);
break;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
void hdd_reset_pwrparams(hdd_context_t *pHddCtx)
{
/*Switch back to DTIM 1*/
tSirSetPowerParamsReq powerRequest = { 0 };
powerRequest.uIgnoreDTIM = pHddCtx->hdd_actual_ignore_DTIM_value;
powerRequest.uListenInterval = pHddCtx->hdd_actual_LI_value;
powerRequest.uMaxLIModulatedDTIM = pHddCtx->cfg_ini->fMaxLIModulatedDTIM;
/* Update ignoreDTIM and ListedInterval in CFG with default values */
ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, powerRequest.uIgnoreDTIM,
NULL, eANI_BOOLEAN_FALSE);
ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, powerRequest.uListenInterval,
NULL, eANI_BOOLEAN_FALSE);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Switch to DTIM%d",powerRequest.uListenInterval);
sme_SetPowerParams( pHddCtx->hHal, &powerRequest, TRUE);
}
VOS_STATUS hdd_enable_bmps_imps(hdd_context_t *pHddCtx)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
if (WLAN_HDD_IS_UNLOAD_IN_PROGRESS(pHddCtx))
{
hddLog( LOGE, FL("Wlan Unload in progress"));
return VOS_STATUS_E_PERM;
}
if (wlan_hdd_check_monitor_state(pHddCtx)) {
hddLog(LOG1, FL("Monitor mode is started, cannot enable BMPS"));
return VOS_STATUS_SUCCESS;
}
if(pHddCtx->cfg_ini->fIsBmpsEnabled)
{
sme_EnablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);
}
if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled)
{
sme_StartAutoBmpsTimer(pHddCtx->hHal);
}
if (pHddCtx->cfg_ini->fIsImpsEnabled)
{
sme_EnablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
}
return status;
}
VOS_STATUS hdd_disable_bmps_imps(hdd_context_t *pHddCtx, tANI_U8 session_type)
{
hdd_adapter_t *pAdapter = NULL;
eHalStatus halStatus;
VOS_STATUS status = VOS_STATUS_E_INVAL;
v_BOOL_t disableBmps = FALSE;
v_BOOL_t disableImps = FALSE;
switch(session_type)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_GO:
//Exit BMPS -> Is Sta/P2P Client is already connected
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
if((NULL != pAdapter)&&
hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))
{
disableBmps = TRUE;
}
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_CLIENT);
if((NULL != pAdapter)&&
hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))
{
disableBmps = TRUE;
}
//Exit both Bmps and Imps incase of Go/SAP Mode
if((WLAN_HDD_SOFTAP == session_type) ||
(WLAN_HDD_P2P_GO == session_type))
{
disableBmps = TRUE;
disableImps = TRUE;
}
if(TRUE == disableImps)
{
if (pHddCtx->cfg_ini->fIsImpsEnabled)
{
sme_DisablePowerSave (pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
}
}
if(TRUE == disableBmps)
{
if(pHddCtx->cfg_ini->fIsBmpsEnabled)
{
halStatus = sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);
if(eHAL_STATUS_SUCCESS != halStatus)
{
status = VOS_STATUS_E_FAILURE;
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Disable Power Save", __func__);
VOS_ASSERT(0);
return status;
}
}
if(pHddCtx->cfg_ini->fIsAutoBmpsTimerEnabled)
{
halStatus = sme_StopAutoBmpsTimer(pHddCtx->hHal);
if(eHAL_STATUS_SUCCESS != halStatus)
{
status = VOS_STATUS_E_FAILURE;
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Stop Auto Bmps Timer", __func__);
VOS_ASSERT(0);
return status;
}
}
}
if((TRUE == disableBmps) ||
(TRUE == disableImps))
{
/* Now, get the chip into Full Power now */
INIT_COMPLETION(pHddCtx->full_pwr_comp_var);
halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_pwr_cbk,
pHddCtx, eSME_FULL_PWR_NEEDED_BY_HDD);
if(halStatus != eHAL_STATUS_SUCCESS)
{
if(halStatus == eHAL_STATUS_PMC_PENDING)
{
long ret;
//Block on a completion variable. Can't wait forever though
ret = wait_for_completion_interruptible_timeout(
&pHddCtx->full_pwr_comp_var,
msecs_to_jiffies(1000));
if (ret <= 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: wait on full_pwr_comp_var failed %ld",
__func__, ret);
}
}
else
{
status = VOS_STATUS_E_FAILURE;
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Request for Full Power failed", __func__);
VOS_ASSERT(0);
return status;
}
}
status = VOS_STATUS_SUCCESS;
}
break;
}
return status;
}
void hdd_mon_post_msg_cb(void *context)
{
struct hdd_request *request;
request = hdd_request_get(context);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
return;
}
hdd_request_complete(request);
hdd_request_put(request);
}
void hdd_init_mon_mode (hdd_adapter_t *pAdapter)
{
hdd_mon_ctx_t *pMonCtx = NULL;
spin_lock_init(&pAdapter->sta_hash_lock);
pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
pMonCtx = WLAN_HDD_GET_MONITOR_CTX_PTR(pAdapter);
pMonCtx->state = 0;
pMonCtx->ChannelNo = 1;
pMonCtx->ChannelBW = 20;
pMonCtx->crcCheckEnabled = 1;
pMonCtx->typeSubtypeBitmap = 0xFFFF00000000;
pMonCtx->is80211to803ConReq = 1;
pMonCtx->numOfMacFilters = 0;
}
hdd_adapter_t* hdd_open_adapter( hdd_context_t *pHddCtx, tANI_U8 session_type,
const char *iface_name, tSirMacAddr macAddr,
tANI_U8 rtnl_held )
{
hdd_adapter_t *pAdapter = NULL;
hdd_adapter_list_node_t *pHddAdapterNode = NULL;
VOS_STATUS status = VOS_STATUS_E_FAILURE;
VOS_STATUS exitbmpsStatus;
v_CONTEXT_t pVosContext = NULL;
/* No need to check for NULL, reaching this step
* means vos context is initialized
*/
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s iface =%s type = %d",__func__,iface_name,session_type);
if(macAddr == NULL)
{
/* Not received valid macAddr */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:Unable to add virtual intf: Not able to get"
"valid mac address",__func__);
return NULL;
}
//Disable BMPS incase of Concurrency
exitbmpsStatus = hdd_disable_bmps_imps(pHddCtx, session_type);
if(VOS_STATUS_E_FAILURE == exitbmpsStatus)
{
//Fail to Exit BMPS
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Fail to Exit BMPS", __func__);
VOS_ASSERT(0);
return NULL;
}
switch(session_type)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
{
pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );
if( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"), session_type);
return NULL;
}
#ifdef FEATURE_WLAN_TDLS
/* A Mutex Lock is introduced while changing/initializing the mode to
* protect the concurrent access for the Adapters by TDLS module.
*/
mutex_lock(&pHddCtx->tdls_lock);
#endif
pAdapter->wdev.iftype = (session_type == WLAN_HDD_P2P_CLIENT) ?
NL80211_IFTYPE_P2P_CLIENT:
NL80211_IFTYPE_STATION;
pAdapter->device_mode = session_type;
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
hdd_initialize_adapter_common(pAdapter);
status = hdd_register_interface( pAdapter, rtnl_held );
if( VOS_STATUS_SUCCESS != status )
{
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
#endif
hdd_deinit_adapter(pHddCtx, pAdapter, rtnl_held);
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
goto err_free_netdev;
}
// Workqueue which gets scheduled in IPv4 notification callback.
vos_init_work(&pAdapter->ipv4NotifierWorkQueue, hdd_ipv4_notifier_work_queue);
#ifdef WLAN_NS_OFFLOAD
// Workqueue which gets scheduled in IPv6 notification callback.
vos_init_work(&pAdapter->ipv6NotifierWorkQueue, hdd_ipv6_notifier_work_queue);
#endif
//Stop the Interface TX queue.
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
//netif_tx_disable(pWlanDev);
netif_carrier_off(pAdapter->dev);
if (WLAN_HDD_P2P_CLIENT == session_type ||
WLAN_HDD_P2P_DEVICE == session_type)
{
/* Initialize the work queue to defer the
* back to back RoC request */
vos_init_delayed_work(&pAdapter->roc_work,
hdd_p2p_roc_work_queue);
}
break;
}
case WLAN_HDD_P2P_GO:
case WLAN_HDD_SOFTAP:
{
pAdapter = hdd_wlan_create_ap_dev( pHddCtx, macAddr, (tANI_U8 *)iface_name );
if( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"), session_type);
return NULL;
}
pAdapter->wdev.iftype = (session_type == WLAN_HDD_SOFTAP) ?
NL80211_IFTYPE_AP:
NL80211_IFTYPE_P2P_GO;
pAdapter->device_mode = session_type;
hdd_initialize_adapter_common(pAdapter);
status = hdd_sta_id_hash_attach(pAdapter);
if (VOS_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to attach hash for session %d"), session_type);
goto err_free_netdev;
}
status = hdd_register_hostapd( pAdapter, rtnl_held );
if( VOS_STATUS_SUCCESS != status )
{
hdd_deinit_adapter(pHddCtx, pAdapter, rtnl_held);
goto err_free_netdev;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
netif_carrier_off(pAdapter->dev);
hdd_set_conparam( 1 );
// Workqueue which gets scheduled in IPv4 notification callback.
vos_init_work(&pAdapter->ipv4NotifierWorkQueue,
hdd_ipv4_notifier_work_queue);
#ifdef WLAN_NS_OFFLOAD
// Workqueue which gets scheduled in IPv6 notification callback.
vos_init_work(&pAdapter->ipv6NotifierWorkQueue,
hdd_ipv6_notifier_work_queue);
#endif
if (WLAN_HDD_P2P_GO == session_type)
{
/* Initialize the work queue to
* defer the back to back RoC request */
INIT_DELAYED_WORK(&pAdapter->roc_work,
hdd_p2p_roc_work_queue);
}
break;
}
case WLAN_HDD_MONITOR:
{
pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );
if( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"), session_type);
return NULL;
}
pAdapter->device_mode = session_type;
pAdapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
// Register wireless extensions
if( VOS_STATUS_SUCCESS != (status = hdd_register_wext(pAdapter->dev)))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"hdd_register_wext() failed with status code %08d [x%08x]",
status, status );
status = VOS_STATUS_E_FAILURE;
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)
pAdapter->dev->netdev_ops = &wlan_mon_drv_ops;
#else
pAdapter->dev->open = hdd_mon_open;
pAdapter->dev->hard_start_xmit = hdd_mon_hard_start_xmit;
pAdapter->dev->stop = hdd_mon_stop;
pAdapter->dev->do_ioctl = hdd_mon_ioctl;
#endif
hdd_init_mon_mode( pAdapter );
hdd_initialize_adapter_common(pAdapter);
hdd_init_tx_rx( pAdapter );
if (VOS_MONITOR_MODE != hdd_get_conparam())
WLANTL_SetMonRxCbk( pVosContext, hdd_rx_packet_monitor_cbk );
set_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags);
status = hdd_register_interface( pAdapter, rtnl_held );
//Stop the Interface TX queue.
netif_tx_disable(pAdapter->dev);
netif_carrier_off(pAdapter->dev);
}
break;
case WLAN_HDD_FTM:
{
pAdapter = hdd_alloc_station_adapter( pHddCtx, macAddr, iface_name );
if( NULL == pAdapter )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"), session_type);
return NULL;
}
/* Assign NL80211_IFTYPE_STATION as interface type to resolve Kernel Warning
* message while loading driver in FTM mode. */
pAdapter->wdev.iftype = NL80211_IFTYPE_STATION;
pAdapter->device_mode = session_type;
status = hdd_register_interface( pAdapter, rtnl_held );
hdd_initialize_adapter_common(pAdapter);
hdd_init_tx_rx( pAdapter );
//Stop the Interface TX queue.
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
netif_carrier_off(pAdapter->dev);
}
break;
default:
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s Invalid session type %d",
__func__, session_type);
VOS_ASSERT(0);
return NULL;
}
}
if( VOS_STATUS_SUCCESS == status )
{
//Add it to the hdd's session list.
pHddAdapterNode = vos_mem_malloc( sizeof( hdd_adapter_list_node_t ) );
if( NULL == pHddAdapterNode )
{
status = VOS_STATUS_E_NOMEM;
}
else
{
pHddAdapterNode->pAdapter = pAdapter;
status = hdd_add_adapter_back ( pHddCtx,
pHddAdapterNode );
}
}
if( VOS_STATUS_SUCCESS != status )
{
if( NULL != pAdapter )
{
hdd_cleanup_adapter( pHddCtx, pAdapter, rtnl_held );
pAdapter = NULL;
}
if( NULL != pHddAdapterNode )
{
vos_mem_free( pHddAdapterNode );
}
goto resume_bmps;
}
if(VOS_STATUS_SUCCESS == status)
{
wlan_hdd_set_concurrency_mode(pHddCtx, session_type);
//Initialize the WoWL service
if(!hdd_init_wowl(pAdapter))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_init_wowl failed",__func__);
goto err_free_netdev;
}
//Initialize the TSF capture data
wlan_hdd_tsf_init(pAdapter);
}
return pAdapter;
err_free_netdev:
wlan_hdd_release_intf_addr( pHddCtx,
pAdapter->macAddressCurrent.bytes );
free_netdev(pAdapter->dev);
resume_bmps:
//If bmps disabled enable it
if(VOS_STATUS_SUCCESS == exitbmpsStatus)
{
if (pHddCtx->hdd_wlan_suspended)
{
hdd_set_pwrparams(pHddCtx);
}
hdd_enable_bmps_imps(pHddCtx);
}
return NULL;
}
VOS_STATUS hdd_close_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter,
tANI_U8 rtnl_held )
{
hdd_adapter_list_node_t *pAdapterNode, *pCurrent, *pNext;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pCurrent );
if( VOS_STATUS_SUCCESS != status )
{
hddLog(VOS_TRACE_LEVEL_WARN,"%s: adapter list empty %d",
__func__, status);
return status;
}
while ( pCurrent->pAdapter != pAdapter )
{
status = hdd_get_next_adapter ( pHddCtx, pCurrent, &pNext );
if( VOS_STATUS_SUCCESS != status )
break;
pCurrent = pNext;
}
pAdapterNode = pCurrent;
if( VOS_STATUS_SUCCESS == status )
{
wlan_hdd_clear_concurrency_mode(pHddCtx, pAdapter->device_mode);
hdd_cleanup_adapter( pHddCtx, pAdapterNode->pAdapter, rtnl_held );
#ifdef FEATURE_WLAN_TDLS
/* A Mutex Lock is introduced while changing/initializing the mode to
* protect the concurrent access for the Adapters by TDLS module.
*/
mutex_lock(&pHddCtx->tdls_lock);
#endif
hdd_remove_adapter( pHddCtx, pAdapterNode );
vos_mem_free( pAdapterNode );
pAdapterNode = NULL;
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
/* If there is a single session of STA/P2P client, re-enable BMPS */
if ((!vos_concurrent_open_sessions_running()) &&
((pHddCtx->no_of_open_sessions[VOS_STA_MODE] >= 1) ||
(pHddCtx->no_of_open_sessions[VOS_P2P_CLIENT_MODE] >= 1)))
{
if (pHddCtx->hdd_wlan_suspended)
{
hdd_set_pwrparams(pHddCtx);
}
hdd_enable_bmps_imps(pHddCtx);
}
return VOS_STATUS_SUCCESS;
}
return VOS_STATUS_E_FAILURE;
}
VOS_STATUS hdd_close_all_adapters( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pHddAdapterNode;
VOS_STATUS status;
ENTER();
do
{
status = hdd_remove_front_adapter( pHddCtx, &pHddAdapterNode );
if( pHddAdapterNode && VOS_STATUS_SUCCESS == status )
{
hdd_cleanup_adapter( pHddCtx, pHddAdapterNode->pAdapter, FALSE );
vos_mem_free( pHddAdapterNode );
}
}while( NULL != pHddAdapterNode && VOS_STATUS_E_EMPTY != status );
EXIT();
return VOS_STATUS_SUCCESS;
}
void wlan_hdd_reset_prob_rspies(hdd_adapter_t* pHostapdAdapter)
{
v_U8_t addIE[1] = {0};
if ( eHAL_STATUS_FAILURE == ccmCfgSetStr((WLAN_HDD_GET_CTX(pHostapdAdapter))->hHal,
WNI_CFG_PROBE_RSP_ADDNIE_DATA1,(tANI_U8*)addIE, 0, NULL,
eANI_BOOLEAN_FALSE) )
{
hddLog(LOGE,
"Could not pass on WNI_CFG_PROBE_RSP_ADDNIE_DATA1 to CCM");
}
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 ( 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");
}
}
VOS_STATUS hdd_cleanup_ap_events(hdd_adapter_t *adapter)
{
#ifdef DHCP_SERVER_OFFLOAD
vos_event_destroy(&adapter->dhcp_status.vos_event);
#endif
#ifdef MDNS_OFFLOAD
vos_event_destroy(&adapter->mdns_status.vos_event);
#endif
return VOS_STATUS_SUCCESS;
}
int wlan_hdd_stop_mon(hdd_context_t *hdd_ctx, bool wait)
{
hdd_adapter_t *adapter;
hdd_mon_ctx_t *mon_ctx;
void (*func_ptr)(void *context) = NULL;
int ret = 0;
void *cookie = NULL;
struct hdd_request *request;
static const struct hdd_request_params params = {
.priv_size = 0,
.timeout_ms = MON_MODE_MSG_TIMEOUT,
};
adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_MONITOR);
if (!adapter) {
hddLog(LOGE, FL("Invalid STA + MON mode"));
return -EINVAL;
}
mon_ctx = WLAN_HDD_GET_MONITOR_CTX_PTR(adapter);
if (!mon_ctx)
return 0;
if (mon_ctx->state != MON_MODE_START)
return 0;
mon_ctx->state = MON_MODE_STOP;
if (wait) {
func_ptr = hdd_mon_post_msg_cb;
request = hdd_request_alloc(&params);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Request allocation failure"));
return -ENOMEM;
}
cookie = hdd_request_cookie(request);
}
/*
* If func_ptr is NULL, on receiving WDI_MON_START_RSP or
* WDI_MON_STOP_RSP hdd_mon_post_msg_cb() won't be invoked
* and so uninitialized cookie won't be accessed.
*/
if (VOS_STATUS_SUCCESS != wlan_hdd_mon_postMsg(cookie,
mon_ctx,
func_ptr)) {
hddLog(LOGE, FL("failed to stop MON MODE"));
ret = -EINVAL;
}
if (!wait)
goto bmps_roaming;
if (!ret)
ret = hdd_request_wait_for_response(request);
hdd_request_put(request);
if (ret) {
hddLog(LOGE,
FL("timeout on stop monitor mode completion %d"), ret);
return -EINVAL;
}
bmps_roaming:
hddLog(LOG1, FL("Enable BMPS"));
hdd_enable_bmps_imps(hdd_ctx);
hdd_restore_roaming(hdd_ctx);
return 0;
}
bool wlan_hdd_check_monitor_state(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *mon_adapter;
hdd_mon_ctx_t *mon_ctx;
if (hdd_ctx->concurrency_mode != VOS_STA_MON)
return false;
mon_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_MONITOR);
if (!mon_adapter) {
hddLog(LOGE, FL("Invalid concurrency mode"));
return false;
}
mon_ctx = WLAN_HDD_GET_MONITOR_CTX_PTR(mon_adapter);
if (mon_ctx->state == MON_MODE_START)
return true;
return false;
}
int wlan_hdd_check_and_stop_mon(hdd_adapter_t *sta_adapter, bool wait)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(sta_adapter);
if ((sta_adapter->device_mode != WLAN_HDD_INFRA_STATION) ||
!wlan_hdd_check_monitor_state(hdd_ctx))
return 0;
if (wlan_hdd_stop_mon(hdd_ctx, wait))
return -EINVAL;
return 0;
}
void hdd_disable_roaming(hdd_context_t *hdd_ctx)
{
if (!hdd_ctx)
return;
if (!hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled) {
hdd_ctx->roaming_ini_original = CFG_LFR_FEATURE_ENABLED_MIN;
return;
}
hddLog(LOG1, FL("Disable driver and firmware roaming"));
hdd_ctx->roaming_ini_original =
hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled;
hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled =
CFG_LFR_FEATURE_ENABLED_MIN;
sme_UpdateIsFastRoamIniFeatureEnabled(hdd_ctx->hHal,
CFG_LFR_FEATURE_ENABLED_MIN);
}
void hdd_restore_roaming(hdd_context_t *hdd_ctx)
{
if (!hdd_ctx->roaming_ini_original)
return;
hddLog(LOG1, FL("Enable driver and firmware roaming"));
hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled =
CFG_LFR_FEATURE_ENABLED_MAX;
hdd_ctx->roaming_ini_original = CFG_LFR_FEATURE_ENABLED_MIN;
sme_UpdateIsFastRoamIniFeatureEnabled(hdd_ctx->hHal,
CFG_LFR_FEATURE_ENABLED_MAX);
}
VOS_STATUS hdd_stop_adapter( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter,
const v_BOOL_t bCloseSession )
{
eHalStatus halStatus = eHAL_STATUS_SUCCESS;
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
hdd_scaninfo_t *pScanInfo = NULL;
union iwreq_data wrqu;
v_U8_t retry = 0;
long ret;
VOS_STATUS status;
if (pHddCtx->isLogpInProgress) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s:LOGP in Progress. Ignore!!!",__func__);
return VOS_STATUS_E_FAILURE;
}
ENTER();
pScanInfo = &pHddCtx->scan_info;
switch(pAdapter->device_mode)
{
case WLAN_HDD_IBSS:
if ( VOS_TRUE == bCloseSession )
{
status = hdd_sta_id_hash_detach(pAdapter);
if (status != VOS_STATUS_SUCCESS)
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sta id hash detach failed"));
}
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
{
hdd_station_ctx_t *pstation = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_exit(pAdapter, TRUE);
mutex_unlock(&pHddCtx->tdls_lock);
#endif
if(hdd_connIsConnected(pstation) ||
(pstation->conn_info.connState == eConnectionState_Connecting))
{
if (pWextState->roamProfile.BSSType == eCSR_BSS_TYPE_START_IBSS)
{
INIT_COMPLETION(pAdapter->disconnect_comp_var);
halStatus = sme_RoamDisconnect(pHddCtx->hHal,
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_IBSS_LEAVE);
/* Success implies disconnect command got queued up successfully
* Or cmd not queued as scan for SSID is in progress
*/
if((eHAL_STATUS_SUCCESS == halStatus) ||
(eHAL_STATUS_CMD_NOT_QUEUED == halStatus))
{
ret = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (ret <= 0 &&
(eHAL_STATUS_CMD_NOT_QUEUED != halStatus))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: wait on disconnect_comp_var failed %ld",
__func__, ret);
}
}
else
{
hddLog(LOGE, "%s: failed to post disconnect event to SME",
__func__);
}
}
else
{
wlan_hdd_disconnect(pAdapter, eCSR_DISCONNECT_REASON_DEAUTH);
}
memset(&wrqu, '\0', sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN);
wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL);
}
else if(pstation->conn_info.connState ==
eConnectionState_Disconnecting)
{
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);
}
}
if(pScanInfo != NULL && pHddCtx->scan_info.mScanPending)
{
wlan_hdd_scan_abort(pAdapter);
}
if ((pAdapter->device_mode != WLAN_HDD_INFRA_STATION) &&
(pAdapter->device_mode != WLAN_HDD_IBSS))
{
while (pAdapter->is_roc_inprogress)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: ROC in progress for session %d!!!",
__func__, pAdapter->sessionId);
// waiting for ROC to expire
msleep(500);
/* In GO present case , if retry exceeds 3,
it means something went wrong. */
if ( retry++ > MAX_WAIT_FOR_ROC_COMPLETION )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: ROC completion is not received.!!!", __func__);
if (eHAL_STATUS_SUCCESS !=
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter),
pAdapter->sessionId ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Failed to Cancel Remain on Channel"));
}
wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
break;
}
}
vos_flush_delayed_work(&pAdapter->roc_work);
}
#ifdef WLAN_NS_OFFLOAD
vos_flush_work(&pAdapter->ipv6NotifierWorkQueue);
#endif
vos_flush_work(&pAdapter->ipv4NotifierWorkQueue);
/* It is possible that the caller of this function does not
* wish to close the session
*/
if (VOS_TRUE == bCloseSession &&
test_bit(SME_SESSION_OPENED, &pAdapter->event_flags))
{
INIT_COMPLETION(pAdapter->session_close_comp_var);
if (eHAL_STATUS_SUCCESS ==
sme_CloseSession(pHddCtx->hHal, pAdapter->sessionId, FALSE,
VOS_FALSE, hdd_smeCloseSessionCallback, pAdapter))
{
unsigned long ret;
//Block on a completion variable. Can't wait forever though.
ret = wait_for_completion_timeout(
&pAdapter->session_close_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_SESSIONOPENCLOSE));
if ( 0 >= ret)
{
hddLog(LOGE, "%s: failure waiting for session_close_comp_var %ld",
__func__, ret);
}
}
}
}
break;
case WLAN_HDD_SOFTAP:
/* Delete all associated STAs before stopping AP */
if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
hdd_del_all_sta(pAdapter);
/* Fall through */
case WLAN_HDD_P2P_GO:
if ( VOS_TRUE == bCloseSession )
{
status = hdd_sta_id_hash_detach(pAdapter);
if (status != VOS_STATUS_SUCCESS)
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sta id hash detach failed"));
}
//Any softap specific cleanup here...
hdd_cleanup_ap_events(pAdapter);
if (pAdapter->device_mode == WLAN_HDD_P2P_GO) {
while (pAdapter->is_roc_inprogress) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: ROC in progress for session %d!!!",
__func__, pAdapter->sessionId);
msleep(500);
if ( retry++ > MAX_WAIT_FOR_ROC_COMPLETION ) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: ROC completion is not received.!!!", __func__);
WLANSAP_CancelRemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
break;
}
}
vos_flush_delayed_work(&pAdapter->roc_work);
}
#ifdef SAP_AUTH_OFFLOAD
if (pHddCtx->cfg_ini->enable_sap_auth_offload)
hdd_set_sap_auth_offload(pAdapter, FALSE);
#endif
mutex_lock(&pHddCtx->sap_lock);
if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
{
VOS_STATUS status;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_hostapd_state_t *pHostapdState =
WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
vos_event_reset(&pHostapdState->vosEvent);
//Stop Bss.
status = WLANSAP_StopBss(pHddCtx->pvosContext);
if (VOS_IS_STATUS_SUCCESS(status))
{
status = vos_wait_single_event(&pHostapdState->vosEvent, 10000);
if (!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(LOGE, "%s: failure waiting for WLANSAP_StopBss %d",
__func__, status);
}
}
else
{
hddLog(LOGE, "%s: failure in WLANSAP_StopBss", __func__);
}
clear_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags);
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
if (eHAL_STATUS_FAILURE ==
ccmCfgSetInt(pHddCtx->hHal, WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG,
0, NULL, eANI_BOOLEAN_FALSE))
{
hddLog(LOGE,
"%s: Failed to set WNI_CFG_PROBE_RSP_BCN_ADDNIE_FLAG",
__func__);
}
if ( eHAL_STATUS_FAILURE == ccmCfgSetInt((WLAN_HDD_GET_CTX(pAdapter))->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);
kfree(pAdapter->sessionCtx.ap.beacon);
pAdapter->sessionCtx.ap.beacon = NULL;
}
mutex_unlock(&pHddCtx->sap_lock);
#ifdef WLAN_NS_OFFLOAD
vos_flush_work(&pAdapter->ipv6NotifierWorkQueue);
#endif
vos_flush_work(&pAdapter->ipv4NotifierWorkQueue);
break;
case WLAN_HDD_MONITOR:
if (VOS_MONITOR_MODE != hdd_get_conparam())
wlan_hdd_stop_mon(pHddCtx, true);
break;
default:
break;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
/**
* wlan_hdd_restart_sap() - to restart SAP in driver internally
* @ap_adapter: - Pointer to SAP hdd_adapter_t structure
*
* wlan_hdd_restart_sap first delete SAP and do cleanup.
* After that WLANSAP_StartBss start re-start process of SAP.
*
* Return: None
*/
static void wlan_hdd_restart_sap(hdd_adapter_t *ap_adapter)
{
hdd_ap_ctx_t *pHddApCtx;
hdd_hostapd_state_t *pHostapdState;
VOS_STATUS vos_status;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(ap_adapter);
#ifdef USE_CFG80211_DEL_STA_V2
struct station_del_parameters delStaParams;
#endif
tsap_Config_t *pConfig;
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
pConfig = &pHddApCtx->sapConfig;
mutex_lock(&pHddCtx->sap_lock);
if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) {
#ifdef USE_CFG80211_DEL_STA_V2
delStaParams.mac = NULL;
delStaParams.subtype = SIR_MAC_MGMT_DEAUTH >> 4;
delStaParams.reason_code = eCsrForcedDeauthSta;
wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev,
&delStaParams);
#else
wlan_hdd_cfg80211_del_station(ap_adapter->wdev.wiphy, ap_adapter->dev,
NULL);
#endif
hdd_cleanup_actionframe(pHddCtx, ap_adapter);
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter);
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(pHddCtx->pvosContext)) {
vos_status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
goto end;
}
}
clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags);
wlan_hdd_decr_active_session(pHddCtx, ap_adapter->device_mode);
hddLog(LOG1, FL("SAP Stop Success"));
if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) {
hddLog(LOGE, FL("SAP Not able to set AP IEs"));
goto end;
}
vos_event_reset(&pHostapdState->vosEvent);
if (WLANSAP_StartBss(pHddCtx->pvosContext, hdd_hostapd_SAPEventCB,
pConfig, (v_PVOID_t)ap_adapter->dev) != VOS_STATUS_SUCCESS) {
hddLog(LOGE, FL("SAP Start Bss fail"));
goto end;
}
hddLog(LOG1, FL("Waiting for SAP to start"));
vos_status = vos_wait_single_event(&pHostapdState->vosEvent, 10000);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
hddLog(LOGE, FL("SAP Start failed"));
goto end;
}
hddLog(LOG1, FL("SAP Start Success"));
set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags);
wlan_hdd_incr_active_session(pHddCtx, ap_adapter->device_mode);
pHostapdState->bCommit = TRUE;
if (!VOS_IS_STATUS_SUCCESS(hdd_dhcp_mdns_offload(ap_adapter))) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("DHCP/MDNS offload Failed!!"));
vos_event_reset(&pHostapdState->vosEvent);
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(pHddCtx->pvosContext)) {
vos_status = vos_wait_single_event(&pHostapdState->vosEvent,
10000);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
hddLog(LOGE, FL("SAP Stop Failed"));
goto end;
}
}
}
}
end:
mutex_unlock(&pHddCtx->sap_lock);
return;
}
/**
* __hdd_sap_restart_handle() - to handle restarting of SAP
* @work: name of the work
*
* Purpose of this function is to trigger sap start. this function
* will be called from workqueue.
*
* Return: void.
*/
static void __hdd_sap_restart_handle(struct work_struct *work)
{
hdd_adapter_t *sap_adapter;
hdd_context_t *hdd_ctx = container_of(work,
hdd_context_t,
sap_start_work);
if (0 != wlan_hdd_validate_context(hdd_ctx)) {
vos_ssr_unprotect(__func__);
return;
}
sap_adapter = hdd_get_adapter(hdd_ctx,
WLAN_HDD_SOFTAP);
if (sap_adapter == NULL) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("sap_adapter is NULL"));
vos_ssr_unprotect(__func__);
return;
}
if (hdd_ctx->is_ch_avoid_in_progress) {
sap_adapter->sessionCtx.ap.sapConfig.channel = AUTO_CHANNEL_SELECT;
wlan_hdd_restart_sap(sap_adapter);
hdd_change_ch_avoidance_status(hdd_ctx, false);
}
if (hdd_ctx->cfg_ini->enable_sap_auth_offload)
wlan_hdd_restart_sap(sap_adapter);
}
/**
* hdd_sap_restart_handle() - to handle restarting of SAP
* @work: name of the work
*
* Purpose of this function is to trigger sap start. this function
* will be called from workqueue.
*
* Return: void.
*/
static void hdd_sap_restart_handle(struct work_struct *work)
{
vos_ssr_protect(__func__);
__hdd_sap_restart_handle(work);
vos_ssr_unprotect(__func__);
}
/**
* __hdd_force_scc_with_ecsa_handle() - to handle force scc using ecsa
* @work: name of the work
*
* Purpose of this function is to force SCC using ECSA. This function
* will be called from workqueue.
*
* Return: void.
*/
static void
__hdd_force_scc_with_ecsa_handle(struct work_struct *work)
{
hdd_adapter_t *sap_adapter;
hdd_station_ctx_t *sta_ctx;
hdd_adapter_t *sta_adapter;
ptSapContext sap_ctx = NULL;
v_CONTEXT_t vos_ctx;
tANI_U8 target_channel;
tsap_Config_t *sap_config;
bool sta_sap_scc_on_dfs_chan;
eNVChannelEnabledType chan_state;
hdd_context_t *hdd_ctx = container_of(to_delayed_work(work),
hdd_context_t,
ecsa_chan_change_work);
if (wlan_hdd_validate_context(hdd_ctx))
return;
sap_adapter = hdd_get_adapter(hdd_ctx,
WLAN_HDD_SOFTAP);
if (!sap_adapter) {
hddLog(LOGE, FL("sap_adapter is NULL"));
return;
}
vos_ctx = hdd_ctx->pvosContext;
if (!vos_ctx) {
hddLog(LOGE, FL("vos_ctx is NULL"));
return;
}
sap_ctx = VOS_GET_SAP_CB(vos_ctx);
if (!sap_ctx) {
hddLog(LOGE, FL("sap_ctx is NULL"));
return;
}
sap_config = &sap_adapter->sessionCtx.ap.sapConfig;
sta_sap_scc_on_dfs_chan = hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_ctx);
sta_adapter = hdd_get_adapter(hdd_ctx,
WLAN_HDD_INFRA_STATION);
if (!sta_adapter) {
hddLog(LOGE, FL("sta_adapter is NULL"));
return;
}
if (wlansap_chk_n_set_chan_change_in_progress(sap_ctx))
return;
INIT_COMPLETION(sap_ctx->ecsa_info.chan_switch_comp);
sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter);
if (sta_ctx->conn_info.connState != eConnectionState_Associated) {
if (sta_ctx->conn_info.connState == eConnectionState_NotConnected) {
chan_state = vos_nv_getChannelEnabledState(sap_ctx->channel);
hddLog(LOG1, FL("sta not in connected state %d, sta_sap_scc_on_dfs_chan %d, chan_state %d"),
sta_ctx->conn_info.connState, sta_sap_scc_on_dfs_chan,
chan_state);
if (sta_sap_scc_on_dfs_chan &&
(chan_state == NV_CHANNEL_DFS)) {
hddLog(LOGE, FL("Switch SAP to user configured channel"));
target_channel = sap_config->user_config_channel;
goto switch_channel;
}
}
goto abort;
}
target_channel = sta_ctx->conn_info.operationChannel;
switch_channel:
hddLog(LOGE, FL("Switch SAP to %d channel"),
target_channel);
if (!wlansap_set_channel_change(vos_ctx, target_channel, true))
return;
abort:
wlansap_reset_chan_change_in_progress(sap_ctx);
complete(&sap_ctx->ecsa_info.chan_switch_comp);
}
/**
* hdd_force_scc_with_ecsa_handle() - to handle force scc using ecsa
* @work: name of the work
*
* Purpose of this function is to force SCC using ECSA. This function
* will be called from workqueue.
*
* Return: void.
*/
static void
hdd_force_scc_with_ecsa_handle(struct work_struct *work)
{
vos_ssr_protect(__func__);
__hdd_force_scc_with_ecsa_handle(work);
vos_ssr_unprotect(__func__);
}
int hdd_wait_for_ecsa_complete(hdd_context_t *hdd_ctx)
{
int ret;
ptSapContext sap_ctx = NULL;
v_CONTEXT_t vos_ctx;
vos_ctx = hdd_ctx->pvosContext;
if (!vos_ctx) {
hddLog(LOGE, FL("vos_ctx is NULL"));
return 0;
}
sap_ctx = VOS_GET_SAP_CB(vos_ctx);
if (!sap_ctx) {
hddLog(LOG1, FL("sap_ctx is NULL"));
return 0;
}
if(!sap_ctx->isSapSessionOpen) {
hddLog(LOG1, FL("sap session not opened, SAP in state %d"),
sap_ctx->sapsMachine);
return 0;
}
if (!wlansap_get_change_in_progress(sap_ctx)) {
hddLog(LOG1, FL("channel switch not in progress"));
return 0;
}
ret = wait_for_completion_timeout(&sap_ctx->ecsa_info.chan_switch_comp,
msecs_to_jiffies(HDD_SAP_CHAN_CNG_WAIT_TIME));
if (!ret)
{
hddLog(LOGE, FL("Timeout waiting for SAP channel switch"));
return ret;
}
return 0;
}
/**
* hdd_is_sta_sap_scc_allowed_on_dfs_chan() - check if sta+sap scc allowed on
* dfs chan
* @hdd_ctx: pointer to hdd context
*
* This function used to check if sta+sap scc allowed on DFS channel.
*
* Return: None
*/
bool hdd_is_sta_sap_scc_allowed_on_dfs_chan(hdd_context_t *hdd_ctx)
{
if (hdd_ctx->cfg_ini->force_scc_with_ecsa &&
hdd_ctx->cfg_ini->sta_sap_scc_on_dfs_chan)
return true;
else
return false;
}
VOS_STATUS hdd_stop_all_adapters( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
ENTER();
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
hdd_stop_adapter( pHddCtx, pAdapter, VOS_TRUE );
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
#ifdef FEATURE_WLAN_BATCH_SCAN
/**---------------------------------------------------------------------------
\brief hdd_deinit_batch_scan () - This function cleans up batch scan data
structures
\param - pAdapter Pointer to HDD adapter
\return - None
--------------------------------------------------------------------------*/
void hdd_deinit_batch_scan(hdd_adapter_t *pAdapter)
{
tHddBatchScanRsp *pNode;
tHddBatchScanRsp *pPrev;
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Adapter context is Null", __func__);
return;
}
pNode = pAdapter->pBatchScanRsp;
while (pNode)
{
pPrev = pNode;
pNode = pNode->pNext;
vos_mem_free((v_VOID_t * )pPrev);
}
pAdapter->pBatchScanRsp = NULL;
pAdapter->numScanList = 0;
pAdapter->batchScanState = eHDD_BATCH_SCAN_STATE_STOPPED;
pAdapter->prev_batch_id = 0;
return;
}
#endif
VOS_STATUS hdd_reset_all_adapters( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
ENTER();
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
if (pHddCtx->cfg_ini->sap_internal_restart &&
pAdapter->device_mode == WLAN_HDD_SOFTAP) {
hddLog(LOG1, FL("driver supports sap restart"));
vos_flush_work(&pHddCtx->sap_start_work);
hdd_sap_indicate_disconnect_for_sta(pAdapter);
hdd_cleanup_actionframe(pHddCtx, pAdapter);
hdd_softap_deinit_tx_rx(pAdapter, true);
hdd_sap_destroy_timers(pAdapter);
} else {
netif_carrier_off(pAdapter->dev);
}
pAdapter->sessionCtx.station.hdd_ReassocScenario = VOS_FALSE;
if (pAdapter->device_mode == WLAN_HDD_MONITOR)
pAdapter->sessionCtx.monitor.state = MON_MODE_STOP;
hdd_deinit_tx_rx(pAdapter);
if(pAdapter->device_mode == WLAN_HDD_IBSS )
hdd_ibss_deinit_tx_rx(pAdapter);
status = hdd_sta_id_hash_detach(pAdapter);
if (status != VOS_STATUS_SUCCESS)
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sta id hash detach failed for session id %d"),
pAdapter->sessionId);
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags))
{
hdd_wmm_adapter_close( pAdapter );
clear_bit(WMM_INIT_DONE, &pAdapter->event_flags);
}
if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
{
clear_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags);
}
#ifdef FEATURE_WLAN_BATCH_SCAN
if (eHDD_BATCH_SCAN_STATE_STARTED == pAdapter->batchScanState)
{
hdd_deinit_batch_scan(pAdapter);
}
#endif
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_exit(pAdapter, TRUE);
mutex_unlock(&pHddCtx->tdls_lock);
#endif
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
/**
* hdd_get_bss_entry() - Get the bss entry matching the chan, bssid and ssid
* @wiphy: wiphy
* @channel: channel of the BSS to find
* @bssid: bssid of the BSS to find
* @ssid: ssid of the BSS to find
* @ssid_len: ssid len of of the BSS to find
*
* The API is a wrapper to get bss from kernel matching the chan,
* bssid and ssid
*
* Return: Void
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) \
&& !defined(WITH_BACKPORTS) && !defined(IEEE80211_PRIVACY)
struct cfg80211_bss* hdd_get_bss_entry(struct wiphy *wiphy,
struct ieee80211_channel *channel,
const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
return cfg80211_get_bss(wiphy, channel, bssid,
ssid, ssid_len,
WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS);
}
#else
struct cfg80211_bss* hdd_get_bss_entry(struct wiphy *wiphy,
struct ieee80211_channel *channel,
const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
return cfg80211_get_bss(wiphy, channel, bssid,
ssid, ssid_len,
IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY);
}
#endif
#if defined(CFG80211_CONNECT_BSS) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) || \
defined (CFG80211_CONNECT_TIMEOUT_REASON_CODE)
/**
* hdd_connect_bss() - helper function to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @bss: information about connected bss
* @req_ie: Request Information Element
* @req_ie_len: len of the req IE
* @resp_ie: Response IE
* @resp_ie_len: len of ht response IE
* @status: status
* @gfp: Kernel Flag
*
* This is a helper function to send connection status to supplicant
* and gets invoked from wrapper API
*
* Return: Void
*/
static void hdd_connect_bss(struct net_device *dev,
const u8 *bssid,
struct cfg80211_bss *bss,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp)
{
cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp, NL80211_TIMEOUT_UNSPECIFIED);
}
#else
/**
* hdd_connect_bss() - helper function to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @bss: information about connected bss
* @req_ie: Request Information Element
* @req_ie_len: len of the req IE
* @resp_ie: Response IE
* @resp_ie_len: len of ht response IE
* @status: status
* @gfp: Kernel Flag
*
* This is a helper function to send connection status to supplicant
* and gets invoked from wrapper API
*
* Return: Void
*/
static void hdd_connect_bss(struct net_device *dev,
const u8 *bssid,
struct cfg80211_bss *bss,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp)
{
cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp);
}
#endif
/**
* hdd_connect_result() - API to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @roam_info: information about connected bss
* @req_ie: Request Information Element
* @req_ie_len: len of the req IE
* @resp_ie: Response IE
* @resp_ie_len: len of ht response IE
* @status: status
* @gfp: Kernel Flag
*
* The API is a wrapper to send connection status to supplicant
*
* Return: Void
*/
void hdd_connect_result(struct net_device *dev,
const u8 *bssid,
tCsrRoamInfo *roam_info,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp)
{
hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev);
struct cfg80211_bss *bss = NULL;
if (WLAN_STATUS_SUCCESS == status) {
struct ieee80211_channel *chan;
int freq;
int chan_no = roam_info->pBssDesc->channelId;;
if (chan_no <= 14)
freq = ieee80211_channel_to_frequency(chan_no,
HDD_NL80211_BAND_2GHZ);
else
freq = ieee80211_channel_to_frequency(chan_no,
HDD_NL80211_BAND_5GHZ);
chan = ieee80211_get_channel(padapter->wdev.wiphy, freq);
bss = hdd_get_bss_entry(padapter->wdev.wiphy,
chan, bssid,
roam_info->u.pConnectedProfile->SSID.ssId,
roam_info->u.pConnectedProfile->SSID.length);
}
hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len, resp_ie, resp_ie_len,
status, gfp);
}
#else
/**
* hdd_connect_result() - API to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @roam_info: information about connected bss
* @req_ie: Request Information Element
* @req_ie_len: len of the req IE
* @resp_ie: Response IE
* @resp_ie_len: len of ht response IE
* @status: status
* @gfp: Kernel Flag
*
* The API is a wrapper to send connection status to supplicant
*
* Return: Void
*/
void hdd_connect_result(struct net_device *dev,
const u8 *bssid,
tCsrRoamInfo *roam_info,
const u8 *req_ie,
size_t req_ie_len,
const u8 * resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp)
{
cfg80211_connect_result(dev, bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp);
}
#endif
VOS_STATUS hdd_start_all_adapters( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
eConnectionState connState;
v_CONTEXT_t pVosContext;
ENTER();
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
hdd_wmm_init( pAdapter );
switch(pAdapter->device_mode)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
connState = (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState;
hdd_init_station_mode(pAdapter);
/* Open the gates for HDD to receive Wext commands */
pAdapter->isLinkUpSvcNeeded = FALSE;
pHddCtx->scan_info.mScanPending = FALSE;
pHddCtx->scan_info.waitScanResult = FALSE;
//Trigger the initial scan
if (!pHddCtx->isLogpInProgress)
hdd_wlan_initial_scan(pAdapter);
//Indicate disconnect event to supplicant if associated previously
if (eConnectionState_Associated == connState ||
eConnectionState_IbssConnected == connState )
{
union iwreq_data wrqu;
memset(&wrqu, '\0', sizeof(wrqu));
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
memset(wrqu.ap_addr.sa_data,'\0',ETH_ALEN);
wireless_send_event(pAdapter->dev, SIOCGIWAP, &wrqu, NULL);
pAdapter->sessionCtx.station.hdd_ReassocScenario = VOS_FALSE;
/* indicate disconnected event to nl80211 */
wlan_hdd_cfg80211_indicate_disconnect(pAdapter->dev, false,
WLAN_REASON_UNSPECIFIED);
}
else if (eConnectionState_Connecting == connState)
{
/*
* Indicate connect failure to supplicant if we were in the
* process of connecting
*/
hdd_connect_result(pAdapter->dev, NULL, NULL,
NULL, 0, NULL, 0,
WLAN_STATUS_ASSOC_DENIED_UNSPEC,
GFP_KERNEL);
}
break;
case WLAN_HDD_SOFTAP:
if (pHddCtx->cfg_ini->sap_internal_restart) {
hdd_init_ap_mode(pAdapter, true);
status = hdd_sta_id_hash_attach(pAdapter);
if (VOS_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to attach hash for"));
}
}
break;
case WLAN_HDD_P2P_GO:
hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send stop ap to supplicant",
__func__);
cfg80211_ap_stopped(pAdapter->dev, GFP_KERNEL);
break;
case WLAN_HDD_MONITOR:
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
hddLog(VOS_TRACE_LEVEL_INFO, FL("[SSR] monitor mode"));
if (!pVosContext) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("vos context is NULL"));
break;
}
hdd_init_tx_rx(pAdapter);
WLANTL_SetMonRxCbk( pVosContext, hdd_rx_packet_monitor_cbk);
break;
default:
break;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
VOS_STATUS hdd_reconnect_all_adapters( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
v_U32_t roamId;
long ret;
ENTER();
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) )
{
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Set HDD connState to eConnectionState_NotConnected",
__func__);
spin_lock_bh(&pAdapter->lock_for_active_session);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState)
{
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
}
pHddStaCtx->conn_info.connState = eConnectionState_NotConnected;
spin_unlock_bh(&pAdapter->lock_for_active_session);
init_completion(&pAdapter->disconnect_comp_var);
sme_RoamDisconnect(pHddCtx->hHal, pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
ret = wait_for_completion_interruptible_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (0 >= ret)
hddLog(LOGE, "%s: failure waiting for disconnect_comp_var %ld",
__func__, ret);
pWextState->roamProfile.csrPersona = pAdapter->device_mode;
pHddCtx->isAmpAllowed = VOS_FALSE;
sme_RoamConnect(pHddCtx->hHal,
pAdapter->sessionId, &(pWextState->roamProfile),
&roamId);
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
void hdd_dump_concurrency_info(hdd_context_t *pHddCtx)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
hdd_station_ctx_t *pHddStaCtx;
hdd_ap_ctx_t *pHddApCtx;
hdd_hostapd_state_t * pHostapdState;
tCsrBssid staBssid = { 0 }, p2pBssid = { 0 }, apBssid = { 0 };
v_U8_t staChannel = 0, p2pChannel = 0, apChannel = 0;
const char *p2pMode = "DEV";
const char *ccMode = "Standalone";
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
switch (pAdapter->device_mode) {
case WLAN_HDD_INFRA_STATION:
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) {
staChannel = pHddStaCtx->conn_info.operationChannel;
memcpy(staBssid, pHddStaCtx->conn_info.bssId, sizeof(staBssid));
}
break;
case WLAN_HDD_P2P_CLIENT:
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) {
p2pChannel = pHddStaCtx->conn_info.operationChannel;
memcpy(p2pBssid, pHddStaCtx->conn_info.bssId, sizeof(p2pBssid));
p2pMode = "CLI";
}
break;
case WLAN_HDD_P2P_GO:
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
if (pHostapdState->bssState == BSS_START && pHostapdState->vosStatus==VOS_STATUS_SUCCESS) {
p2pChannel = pHddApCtx->operatingChannel;
memcpy(p2pBssid, pAdapter->macAddressCurrent.bytes, sizeof(p2pBssid));
}
p2pMode = "GO";
break;
case WLAN_HDD_SOFTAP:
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(pAdapter);
pHostapdState = WLAN_HDD_GET_HOSTAP_STATE_PTR(pAdapter);
if (pHostapdState->bssState == BSS_START && pHostapdState->vosStatus==VOS_STATUS_SUCCESS) {
apChannel = pHddApCtx->operatingChannel;
memcpy(apBssid, pAdapter->macAddressCurrent.bytes, sizeof(apBssid));
}
break;
default:
break;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
if (staChannel > 0 && (apChannel > 0 || p2pChannel > 0)) {
ccMode = (p2pChannel==staChannel||apChannel==staChannel) ? "SCC" : "MCC";
}
hddLog(VOS_TRACE_LEVEL_ERROR, "wlan(%d) " MAC_ADDRESS_STR " %s",
staChannel, MAC_ADDR_ARRAY(staBssid), ccMode);
if (p2pChannel > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "p2p-%s(%d) " MAC_ADDRESS_STR,
p2pMode, p2pChannel, MAC_ADDR_ARRAY(p2pBssid));
}
if (apChannel > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "AP(%d) " MAC_ADDRESS_STR,
apChannel, MAC_ADDR_ARRAY(apBssid));
}
if (p2pChannel > 0 && apChannel > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "Error concurrent SAP %d and P2P %d which is not support", apChannel, p2pChannel);
}
}
bool hdd_is_ssr_required( void)
{
return (isSsrRequired == HDD_SSR_REQUIRED);
}
/* Once SSR is disabled then it cannot be set. */
void hdd_set_ssr_required( e_hdd_ssr_required value)
{
if (HDD_SSR_DISABLED == isSsrRequired)
return;
isSsrRequired = value;
}
void hdd_set_pre_close( hdd_context_t *pHddCtx)
{
sme_PreClose(pHddCtx->hHal);
}
VOS_STATUS hdd_get_front_adapter( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t** ppAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_peek_front ( &pHddCtx->hddAdapters,
(hdd_list_node_t**) ppAdapterNode );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
VOS_STATUS hdd_get_next_adapter( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t* pAdapterNode,
hdd_adapter_list_node_t** pNextAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_peek_next ( &pHddCtx->hddAdapters,
(hdd_list_node_t*) pAdapterNode,
(hdd_list_node_t**)pNextAdapterNode );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
VOS_STATUS hdd_remove_adapter( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t* pAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_remove_node ( &pHddCtx->hddAdapters,
&pAdapterNode->node );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
VOS_STATUS hdd_remove_front_adapter( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t** ppAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_remove_front( &pHddCtx->hddAdapters,
(hdd_list_node_t**) ppAdapterNode );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
VOS_STATUS hdd_add_adapter_back( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t* pAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_insert_back ( &pHddCtx->hddAdapters,
(hdd_list_node_t*) pAdapterNode );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
VOS_STATUS hdd_add_adapter_front( hdd_context_t *pHddCtx,
hdd_adapter_list_node_t* pAdapterNode)
{
VOS_STATUS status;
spin_lock_bh(&pHddCtx->hddAdapters.lock);
status = hdd_list_insert_front ( &pHddCtx->hddAdapters,
(hdd_list_node_t*) pAdapterNode );
spin_unlock_bh(&pHddCtx->hddAdapters.lock);
return status;
}
hdd_adapter_t * hdd_get_adapter_by_macaddr( hdd_context_t *pHddCtx,
tSirMacAddr macAddr )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( pAdapter && vos_mem_compare( pAdapter->macAddressCurrent.bytes,
macAddr, sizeof(tSirMacAddr) ) )
{
return pAdapter;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return NULL;
}
hdd_adapter_t * hdd_get_adapter_by_name( hdd_context_t *pHddCtx, tANI_U8 *name )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( pAdapter && !strncmp( pAdapter->dev->name, (const char *)name,
IFNAMSIZ ) )
{
return pAdapter;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return NULL;
}
hdd_adapter_t *hdd_get_adapter_by_sme_session_id( hdd_context_t *pHddCtx,
tANI_U32 sme_session_id )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS vos_status;
vos_status = hdd_get_front_adapter( pHddCtx, &pAdapterNode);
while ((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == vos_status))
{
pAdapter = pAdapterNode->pAdapter;
if (pAdapter->sessionId == sme_session_id)
return pAdapter;
vos_status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_session_id %d does not exist with host",
__func__, sme_session_id);
return NULL;
}
hdd_adapter_t * hdd_get_adapter( hdd_context_t *pHddCtx, device_mode_t mode )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( pAdapter && (mode == pAdapter->device_mode) )
{
return pAdapter;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return NULL;
}
//Remove this function later
hdd_adapter_t * hdd_get_mon_adapter( hdd_context_t *pHddCtx )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( pAdapter && WLAN_HDD_MONITOR == pAdapter->device_mode )
{
return pAdapter;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return NULL;
}
/**---------------------------------------------------------------------------
\brief hdd_get_operating_channel() -
This API returns the operating channel of the requested device mode
\param - pHddCtx - Pointer to the HDD context.
- mode - Device mode for which operating channel is required
suported modes - WLAN_HDD_INFRA_STATION, WLAN_HDD_P2P_CLIENT
WLAN_HDD_SOFTAP, WLAN_HDD_P2P_GO.
\return - channel number. "0" id the requested device is not found OR it is not connected.
--------------------------------------------------------------------------*/
v_U8_t hdd_get_operating_channel( hdd_context_t *pHddCtx, device_mode_t mode )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
v_U8_t operatingChannel = 0;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if( mode == pAdapter->device_mode )
{
switch(pAdapter->device_mode)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
if( hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )) )
operatingChannel = (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.operationChannel;
break;
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_GO:
/*softap connection info */
if(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
operatingChannel = (WLAN_HDD_GET_AP_CTX_PTR(pAdapter))->operatingChannel;
break;
default:
break;
}
break; //Found the device of interest. break the loop
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return operatingChannel;
}
#ifdef WLAN_FEATURE_PACKET_FILTERING
/**---------------------------------------------------------------------------
\brief __hdd_set_multicast_list() -
This used to set the multicast address list.
\param - dev - Pointer to the WLAN device.
- skb - Pointer to OS packet (sk_buff).
\return - success/fail
--------------------------------------------------------------------------*/
static void __hdd_set_multicast_list(struct net_device *dev)
{
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int mc_count;
int i = 0, ret = 0;
struct netdev_hw_addr *ha;
ENTER();
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Adapter context is Null", __func__);
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return;
}
if (dev->flags & IFF_ALLMULTI)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: allow all multicast frames", __func__);
pAdapter->mc_addr_list.mc_cnt = 0;
}
else
{
mc_count = netdev_mc_count(dev);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: mc_count = %u", __func__, mc_count);
if (mc_count > WLAN_HDD_MAX_MC_ADDR_LIST)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: No free filter available; allow all multicast frames", __func__);
pAdapter->mc_addr_list.mc_cnt = 0;
return;
}
pAdapter->mc_addr_list.mc_cnt = mc_count;
netdev_for_each_mc_addr(ha, dev) {
if (i == mc_count)
break;
memset(&(pAdapter->mc_addr_list.addr[i][0]), 0, ETH_ALEN);
memcpy(&(pAdapter->mc_addr_list.addr[i][0]), ha->addr, ETH_ALEN);
hddLog(VOS_TRACE_LEVEL_INFO, "%s: mlist[%d] = "MAC_ADDRESS_STR,
__func__, i,
MAC_ADDR_ARRAY(pAdapter->mc_addr_list.addr[i]));
i++;
}
}
if (pHddCtx->hdd_wlan_suspended)
{
/*
* Configure the Mcast address list to FW
* If wlan is already in suspend mode
*/
wlan_hdd_set_mc_addr_list(pAdapter, TRUE);
}
EXIT();
return;
}
static void hdd_set_multicast_list(struct net_device *dev)
{
vos_ssr_protect(__func__);
__hdd_set_multicast_list(dev);
vos_ssr_unprotect(__func__);
}
#endif
/**---------------------------------------------------------------------------
\brief hdd_select_queue() -
This function is registered with the Linux OS for network
core to decide which queue to use first.
\param - dev - Pointer to the WLAN device.
- skb - Pointer to OS packet (sk_buff).
\return - ac, Queue Index/access category corresponding to UP in IP header
--------------------------------------------------------------------------*/
v_U16_t hdd_select_queue(struct net_device *dev,
struct sk_buff *skb
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0))
, void *accel_priv
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
, select_queue_fallback_t fallback
#endif
)
{
return hdd_wmm_select_queue(dev, skb);
}
/**---------------------------------------------------------------------------
\brief hdd_wlan_initial_scan() -
This function triggers the initial scan
\param - pAdapter - Pointer to the HDD adapter.
--------------------------------------------------------------------------*/
void hdd_wlan_initial_scan(hdd_adapter_t *pAdapter)
{
tCsrScanRequest scanReq;
tCsrChannelInfo channelInfo;
eHalStatus halStatus;
tANI_U32 scanId;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
vos_mem_zero(&scanReq, sizeof(tCsrScanRequest));
vos_mem_set(&scanReq.bssid, sizeof(tCsrBssid), 0xff);
scanReq.BSSType = eCSR_BSS_TYPE_ANY;
if(sme_Is11dSupported(pHddCtx->hHal))
{
halStatus = sme_ScanGetBaseChannels( pHddCtx->hHal, &channelInfo );
if ( HAL_STATUS_SUCCESS( halStatus ) )
{
scanReq.ChannelInfo.ChannelList = vos_mem_malloc(channelInfo.numOfChannels);
if( !scanReq.ChannelInfo.ChannelList )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s kmalloc failed", __func__);
vos_mem_free(channelInfo.ChannelList);
channelInfo.ChannelList = NULL;
return;
}
vos_mem_copy(scanReq.ChannelInfo.ChannelList, channelInfo.ChannelList,
channelInfo.numOfChannels);
scanReq.ChannelInfo.numOfChannels = channelInfo.numOfChannels;
vos_mem_free(channelInfo.ChannelList);
channelInfo.ChannelList = NULL;
}
scanReq.scanType = eSIR_PASSIVE_SCAN;
scanReq.requestType = eCSR_SCAN_REQUEST_11D_SCAN;
scanReq.maxChnTime = pHddCtx->cfg_ini->nPassiveMaxChnTime;
scanReq.minChnTime = pHddCtx->cfg_ini->nPassiveMinChnTime;
}
else
{
scanReq.scanType = eSIR_ACTIVE_SCAN;
scanReq.requestType = eCSR_SCAN_REQUEST_FULL_SCAN;
scanReq.maxChnTime = pHddCtx->cfg_ini->nActiveMaxChnTime;
scanReq.minChnTime = pHddCtx->cfg_ini->nActiveMinChnTime;
}
halStatus = sme_ScanRequest(pHddCtx->hHal, pAdapter->sessionId, &scanReq, &scanId, NULL, NULL);
if ( !HAL_STATUS_SUCCESS( halStatus ) )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: sme_ScanRequest failed status code %d",
__func__, halStatus );
}
if(sme_Is11dSupported(pHddCtx->hHal))
vos_mem_free(scanReq.ChannelInfo.ChannelList);
}
/**---------------------------------------------------------------------------
\brief hdd_full_power_callback() - HDD full power callback function
This is the function invoked by SME to inform the result of a full power
request issued by HDD
\param - callbackcontext - Pointer to cookie
\param - status - result of request
\return - None
--------------------------------------------------------------------------*/
static void hdd_full_power_callback(void *callbackContext, eHalStatus status)
{
struct statsContext *pContext = callbackContext;
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: context = %pK, status = %d", __func__, pContext, status);
if (NULL == callbackContext)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Bad param, context [%pK]",
__func__, callbackContext);
return;
}
/* there is a race condition that exists between this callback
function and the caller since the caller could time out either
before or while this code is executing. we use a spinlock to
serialize these actions */
spin_lock(&hdd_context_lock);
if (POWER_CONTEXT_MAGIC != pContext->magic)
{
/* the caller presumably timed out so there is nothing we can do */
spin_unlock(&hdd_context_lock);
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Invalid context, magic [%08x]",
__func__, pContext->magic);
return;
}
/* context is valid so caller is still waiting */
/* paranoia: invalidate the magic */
pContext->magic = 0;
/* notify the caller */
complete(&pContext->completion);
/* serialization is complete */
spin_unlock(&hdd_context_lock);
}
void wlan_hdd_mon_set_typesubtype( hdd_mon_ctx_t *pMonCtx,int type)
{
pMonCtx->typeSubtypeBitmap = 0;
if( type%10 ) /* Management Packets */
pMonCtx->typeSubtypeBitmap |= 0xFFFF;
type/=10;
if( type%10 ) /* Control Packets */
pMonCtx->typeSubtypeBitmap |= 0xFFFF0000;
type/=10;
if( type%10 ) /* Data Packets */
pMonCtx->typeSubtypeBitmap |= 0xFFFF00000000;
}
VOS_STATUS wlan_hdd_mon_postMsg(void *context, hdd_mon_ctx_t *pMonCtx,
void* callback)
{
vos_msg_t monMsg;
tSirMonModeReq *pMonModeReq;
if (MON_MODE_START == pMonCtx->state)
monMsg.type = WDA_MON_START_REQ;
else if (MON_MODE_STOP == pMonCtx->state)
monMsg.type = WDA_MON_STOP_REQ;
else {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("invalid monitor state %d"), pMonCtx->state);
return VOS_STATUS_E_FAILURE;
}
pMonModeReq = vos_mem_malloc(sizeof(tSirMonModeReq));
if (pMonModeReq == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("fail to allocate memory for monitor mode req"));
return VOS_STATUS_E_FAILURE;
}
pMonModeReq->context = context;
pMonModeReq->data = pMonCtx;
pMonModeReq->callback = callback;
monMsg.reserved = 0;
monMsg.bodyptr = pMonModeReq;
monMsg.bodyval = 0;
if (VOS_STATUS_SUCCESS != vos_mq_post_message(
VOS_MODULE_ID_WDA,(vos_msg_t *)&monMsg)) {
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: : Failed to post Msg to HAL",__func__);
vos_mem_free(pMonModeReq);
}
return VOS_STATUS_SUCCESS;
}
void wlan_hdd_mon_close(hdd_context_t *pHddCtx)
{
VOS_STATUS vosStatus;
v_CONTEXT_t pVosContext = pHddCtx->pvosContext;
hdd_mon_ctx_t *pMonCtx = NULL;
int ret;
void *cookie;
struct hdd_request *request;
static const struct hdd_request_params params = {
.priv_size = 0,
.timeout_ms = MON_MODE_MSG_TIMEOUT,
};
hdd_adapter_t *pAdapter = hdd_get_adapter(pHddCtx,WLAN_HDD_MONITOR);
if(pAdapter == NULL || pVosContext == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL, "%s:pAdapter is NULL",__func__);
return ;
}
pMonCtx = WLAN_HDD_GET_MONITOR_CTX_PTR(pAdapter);
if (pMonCtx!= NULL && pMonCtx->state == MON_MODE_START) {
pMonCtx->state = MON_MODE_STOP;
request = hdd_request_alloc(&params);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Request allocation failure"));
return;
}
cookie = hdd_request_cookie(request);
if (VOS_STATUS_SUCCESS !=
wlan_hdd_mon_postMsg(cookie, pMonCtx,
hdd_mon_post_msg_cb)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("failed to post MON MODE REQ"));
pMonCtx->state = MON_MODE_START;
goto req_put;
}
ret = hdd_request_wait_for_response(request);
if (ret)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("timeout on monitor mode completion %d"), ret);
req_put:
hdd_request_put(request);
}
hdd_UnregisterWext(pAdapter->dev);
vos_mon_stop( pVosContext );
vosStatus = vos_sched_close( pVosContext );
if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to close VOSS Scheduler",__func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
}
vosStatus = vos_nv_close();
if (!VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to close NV", __func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
}
vos_close(pVosContext);
#ifdef WLAN_KD_READY_NOTIFIER
nl_srv_exit(pHddCtx->ptt_pid);
#else
nl_srv_exit();
#endif
hdd_close_all_adapters( pHddCtx );
}
/**
* hdd_wlan_free_wiphy_channels - free Channel pointer for wiphy
* @ wiphy: the wiphy to validate against
*
* Return: void
*/
void hdd_wlan_free_wiphy_channels(struct wiphy *wiphy)
{
int i =0;
for (i = 0; i < HDD_NUM_NL80211_BANDS; i++)
{
if (NULL != wiphy->bands[i] &&
(NULL != wiphy->bands[i]->channels))
{
vos_mem_free(wiphy->bands[i]->channels);
wiphy->bands[i]->channels = NULL;
}
}
}
/**---------------------------------------------------------------------------
\brief hdd_wlan_exit() - HDD WLAN exit function
This is the driver exit point (invoked during rmmod)
\param - pHddCtx - Pointer to the HDD Context
\return - None
--------------------------------------------------------------------------*/
void hdd_wlan_exit(hdd_context_t *pHddCtx)
{
eHalStatus halStatus;
v_CONTEXT_t pVosContext = pHddCtx->pvosContext;
VOS_STATUS vosStatus;
struct wiphy *wiphy = pHddCtx->wiphy;
hdd_adapter_t* pAdapter = NULL;
struct statsContext powerContext;
long lrc;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
ENTER();
if (VOS_MONITOR_MODE == hdd_get_conparam())
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: MONITOR MODE",__func__);
wlan_hdd_mon_close(pHddCtx);
goto free_hdd_ctx;
}
else if (VOS_FTM_MODE != hdd_get_conparam())
{
// Unloading, restart logic is no more required.
wlan_hdd_restart_deinit(pHddCtx);
#ifdef FEATURE_WLAN_TDLS
/* At the time of driver unloading; if tdls connection is present;
* hdd_rx_packet_cbk calls wlan_hdd_tdls_find_peer.
* wlan_hdd_tdls_find_peer always checks for valid context;
* as load/unload in progress there can be a race condition.
* hdd_rx_packet_cbk calls wlan_hdd_tdls_find_peer only
* when tdls state is enabled.
* As soon as driver set load/unload flag; tdls flag also needs
* to be disabled so that hdd_rx_packet_cbk won't call
* wlan_hdd_tdls_find_peer.
*/
wlan_hdd_tdls_set_mode(pHddCtx, eTDLS_SUPPORT_DISABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_USER);
#endif
vosStatus = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while (NULL != pAdapterNode && VOS_STATUS_E_EMPTY != vosStatus)
{
pAdapter = pAdapterNode->pAdapter;
if (NULL != pAdapter)
{
/* Disable TX on the interface, after this hard_start_xmit() will
* not be called on that interface
*/
hddLog(VOS_TRACE_LEVEL_INFO, FL("Disabling queues"));
netif_tx_disable(pAdapter->dev);
/* Mark the interface status as "down" for outside world */
netif_carrier_off(pAdapter->dev);
/* DeInit the adapter. This ensures that all data packets
* are freed.
*/
#ifdef FEATURE_WLAN_TDLS
mutex_lock(&pHddCtx->tdls_lock);
#endif
hdd_deinit_adapter(pHddCtx, pAdapter, FALSE);
#ifdef FEATURE_WLAN_TDLS
mutex_unlock(&pHddCtx->tdls_lock);
#endif
vos_flush_delayed_work(&pHddCtx->scan_ctxt.scan_work);
wlan_hdd_init_deinit_defer_scan_context(&pHddCtx->scan_ctxt);
if (WLAN_HDD_INFRA_STATION == pAdapter->device_mode ||
WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ||
WLAN_HDD_MONITOR == pAdapter->device_mode)
{
if (WLAN_HDD_INFRA_STATION == pAdapter->device_mode ||
WLAN_HDD_P2P_CLIENT == pAdapter->device_mode)
wlan_hdd_cfg80211_deregister_frames(pAdapter);
hdd_UnregisterWext(pAdapter->dev);
}
}
vosStatus = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
// Cancel any outstanding scan requests. We are about to close all
// of our adapters, but an adapter structure is what SME passes back
// to our callback function. Hence if there are any outstanding scan
// requests then there is a race condition between when the adapter
// is closed and when the callback is invoked.We try to resolve that
// race condition here by canceling any outstanding scans before we
// close the adapters.
// Note that the scans may be cancelled in an asynchronous manner,
// so ideally there needs to be some kind of synchronization. Rather
// than introduce a new synchronization here, we will utilize the
// fact that we are about to Request Full Power, and since that is
// synchronized, the expectation is that by the time Request Full
// Power has completed all scans will be cancelled.
if (pHddCtx->scan_info.mScanPending)
{
if(NULL != pAdapter)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("abort scan mode: %d sessionId: %d"),
pAdapter->device_mode,
pAdapter->sessionId);
}
hdd_abort_mac_scan(pHddCtx,
pHddCtx->scan_info.sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO,"%s: FTM MODE",__func__);
if (pHddCtx->ftm.ftm_state == WLAN_FTM_STARTING)
{
INIT_COMPLETION(pHddCtx->ftm.startCmpVar);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: in middle of FTM START", __func__);
lrc = wait_for_completion_timeout(&pHddCtx->ftm.startCmpVar,
msecs_to_jiffies(20000));
if(!lrc)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: timedout on ftmStartCmpVar fatal error", __func__);
}
}
wlan_hdd_ftm_close(pHddCtx);
goto free_hdd_ctx;
}
/* DeRegister with platform driver as client for Suspend/Resume */
vosStatus = hddDeregisterPmOps(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDeregisterPmOps failed",__func__);
VOS_ASSERT(0);
}
vosStatus = hddDevTmUnregisterNotifyCallback(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmUnregisterNotifyCallback failed",__func__);
}
//Stop the traffic monitor timer
if ((pHddCtx->cfg_ini->dynSplitscan) && (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->tx_rx_trafficTmr)))
{
vos_timer_stop(&pHddCtx->tx_rx_trafficTmr);
}
// Destroy the traffic monitor timer
if ((pHddCtx->cfg_ini->dynSplitscan) &&
(!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pHddCtx->tx_rx_trafficTmr))))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot deallocate Traffic monitor timer", __func__);
}
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
vos_timer_stop(&pHddCtx->delack_timer);
}
if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pHddCtx->delack_timer))) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot deallocate Bus bandwidth timer", __func__);
}
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->tdls_source_timer)) {
vos_timer_stop(&pHddCtx->tdls_source_timer);
}
vos_set_snoc_high_freq_voting(false);
vos_timer_destroy(&pHddCtx->tdls_source_timer);
//Disable IMPS/BMPS as we do not want the device to enter any power
//save mode during shutdown
sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
sme_DisablePowerSave(pHddCtx->hHal, ePMC_BEACON_MODE_POWER_SAVE);
sme_DisablePowerSave(pHddCtx->hHal, ePMC_UAPSD_MODE_POWER_SAVE);
//Ensure that device is in full power as we will touch H/W during vos_Stop
init_completion(&powerContext.completion);
powerContext.magic = POWER_CONTEXT_MAGIC;
halStatus = sme_RequestFullPower(pHddCtx->hHal, hdd_full_power_callback,
&powerContext, eSME_FULL_PWR_NEEDED_BY_HDD);
if (eHAL_STATUS_SUCCESS != halStatus)
{
if (eHAL_STATUS_PMC_PENDING == halStatus)
{
/* request was sent -- wait for the response */
lrc = wait_for_completion_interruptible_timeout(
&powerContext.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_POWER));
if (lrc <= 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: %s while requesting full power",
__func__, (0 == lrc) ? "timeout" : "interrupt");
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Request for Full Power failed, status %d",
__func__, halStatus);
/* continue -- need to clean up as much as possible */
}
}
if ((eHAL_STATUS_SUCCESS == halStatus) ||
(eHAL_STATUS_PMC_PENDING == halStatus && lrc > 0))
{
/* This will issue a dump command which will clean up
BTQM queues and unblock MC thread */
vos_fwDumpReq(274, 0, 0, 0, 0, 1);
}
/* either we never sent a request, we sent a request and received a
response or we sent a request and timed out. if we never sent a
request or if we sent a request and got a response, we want to
clear the magic out of paranoia. if we timed out there is a
race condition such that the callback function could be
executing at the same time we are. of primary concern is if the
callback function had already verified the "magic" but had not
yet set the completion variable when a timeout occurred. we
serialize these activities by invalidating the magic while
holding a shared spinlock which will cause us to block if the
callback is currently executing */
spin_lock(&hdd_context_lock);
powerContext.magic = 0;
spin_unlock(&hdd_context_lock);
/* If Device is shutdown, no point for SME to wait for responses
from device. Pre Close SME */
if(wcnss_device_is_shutdown())
{
sme_PreClose(pHddCtx->hHal);
}
hdd_debugfs_exit(pHddCtx);
#ifdef WLAN_NS_OFFLOAD
hddLog(LOG1, FL("Unregister IPv6 notifier"));
unregister_inet6addr_notifier(&pHddCtx->ipv6_notifier);
#endif
hddLog(LOG1, FL("Unregister IPv4 notifier"));
unregister_inetaddr_notifier(&pHddCtx->ipv4_notifier);
// Unregister the Net Device Notifier
unregister_netdevice_notifier(&hdd_netdev_notifier);
hdd_stop_all_adapters( pHddCtx );
#ifdef WLAN_BTAMP_FEATURE
vosStatus = WLANBAP_Stop(pVosContext);
if (!VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to stop BAP",__func__);
}
#endif //WLAN_BTAMP_FEATURE
//Stop all the modules
vosStatus = vos_stop( pVosContext );
if (!VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to stop VOSS",__func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
if (isSsrPanicOnFailure())
VOS_BUG(0);
}
//This requires pMac access, Call this before vos_close().
hdd_unregister_mcast_bcast_filter(pHddCtx);
//Close the scheduler before calling vos_close to make sure no thread is
// scheduled after the each module close is called i.e after all the data
// structures are freed.
vosStatus = vos_sched_close( pVosContext );
if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to close VOSS Scheduler",__func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
}
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
/* Destroy the wake lock */
vos_wake_lock_destroy(&pHddCtx->rx_wake_lock);
#endif
/* Destroy the wake lock */
vos_wake_lock_destroy(&pHddCtx->sap_wake_lock);
#ifdef CONFIG_ENABLE_LINUX_REG
vosStatus = vos_nv_close();
if (!VOS_IS_STATUS_SUCCESS(vosStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to close NV", __func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( vosStatus ) );
}
#endif
//Close VOSS
//This frees pMac(HAL) context. There should not be any call that requires pMac access after this.
vos_close(pVosContext);
//Close Watchdog
if(pHddCtx->cfg_ini->fIsLogpEnabled)
vos_watchdog_close(pVosContext);
//Clean up HDD Nlink Service
send_btc_nlink_msg(WLAN_MODULE_DOWN_IND, 0);
hdd_close_tx_queues(pHddCtx);
wlan_free_fwr_mem_dump_buffer();
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
if (pHddCtx->cfg_ini->wlanLoggingEnable)
{
wlan_logging_sock_deactivate_svc();
}
#endif
#ifdef WLAN_KD_READY_NOTIFIER
nl_srv_exit(pHddCtx->ptt_pid);
#else
nl_srv_exit();
#endif /* WLAN_KD_READY_NOTIFIER */
#ifdef WLAN_FEATURE_RMC
hdd_close_cesium_nl_sock();
#endif /* WLAN_FEATURE_RMC */
hdd_close_all_adapters( pHddCtx );
vos_flush_delayed_work(&pHddCtx->spoof_mac_addr_work);
vos_flush_delayed_work(&pHddCtx->ecsa_chan_change_work);
vos_flush_work(&pHddCtx->sap_start_work);
free_hdd_ctx:
/* free the power on lock from platform driver */
if (free_riva_power_on_lock("wlan"))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: failed to free power on lock",
__func__);
}
/* Free the cache channels of the command SET_DISABLE_CHANNEL_LIST */
wlan_hdd_free_cache_channels(pHddCtx);
//Free up dynamically allocated members inside HDD Adapter
if (pHddCtx->cfg_ini)
{
kfree(pHddCtx->cfg_ini);
pHddCtx->cfg_ini= NULL;
}
hdd_request_manager_deinit();
/* FTM/MONITOR mode, WIPHY did not registered
If un-register here, system crash will happen */
if (!(VOS_FTM_MODE == hdd_get_conparam() ||
VOS_MONITOR_MODE == hdd_get_conparam()))
{
wiphy_unregister(wiphy) ;
hdd_wlan_free_wiphy_channels(wiphy);
}
wiphy_free(wiphy) ;
if (hdd_is_ssr_required())
{
/* WDI timeout had happened during unload, so SSR is needed here */
subsystem_restart("wcnss");
msleep(5000);
}
hdd_set_ssr_required (VOS_FALSE);
}
/**---------------------------------------------------------------------------
\brief hdd_update_config_from_nv() - Function to update the contents of
the running configuration with parameters taken from NV storage
\param - pHddCtx - Pointer to the HDD global context
\return - VOS_STATUS_SUCCESS if successful
--------------------------------------------------------------------------*/
static VOS_STATUS hdd_update_config_from_nv(hdd_context_t* pHddCtx)
{
v_BOOL_t itemIsValid = VOS_FALSE;
VOS_STATUS status;
v_MACADDR_t macFromNV[VOS_MAX_CONCURRENCY_PERSONA];
v_U8_t macLoop;
/*If the NV is valid then get the macaddress from nv else get it from qcom_cfg.ini*/
status = vos_nv_getValidity(VNV_FIELD_IMAGE, &itemIsValid);
if(status != VOS_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_ERROR," vos_nv_getValidity() failed");
return VOS_STATUS_E_FAILURE;
}
if (itemIsValid == VOS_TRUE)
{
hddLog(VOS_TRACE_LEVEL_INFO_HIGH," Reading the Macaddress from NV");
status = vos_nv_readMultiMacAddress((v_U8_t *)&macFromNV[0].bytes[0],
VOS_MAX_CONCURRENCY_PERSONA);
if(status != VOS_STATUS_SUCCESS)
{
/* Get MAC from NV fail, not update CFG info
* INI MAC value will be used for MAC setting */
hddLog(VOS_TRACE_LEVEL_ERROR," vos_nv_readMacAddress() failed");
return VOS_STATUS_E_FAILURE;
}
/* If first MAC is not valid, treat all others are not valid
* Then all MACs will be got from ini file */
if(vos_is_macaddr_zero(&macFromNV[0]))
{
/* MAC address in NV file is not configured yet */
hddLog(VOS_TRACE_LEVEL_WARN, "Invalid MAC in NV file");
return VOS_STATUS_E_INVAL;
}
/* Get MAC address from NV, update CFG info */
for(macLoop = 0; macLoop < VOS_MAX_CONCURRENCY_PERSONA; macLoop++)
{
if(vos_is_macaddr_zero(&macFromNV[macLoop]))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"not valid MAC from NV for %d", macLoop);
/* This MAC is not valid, skip it
* This MAC will be got from ini file */
}
else
{
vos_mem_copy((v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[macLoop].bytes[0],
(v_U8_t *)&macFromNV[macLoop].bytes[0],
VOS_MAC_ADDR_SIZE);
}
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "NV ITEM, MAC Not valid");
return VOS_STATUS_E_FAILURE;
}
return VOS_STATUS_SUCCESS;
}
/**---------------------------------------------------------------------------
\brief hdd_post_voss_start_config() - HDD post voss start config helper
\param - pAdapter - Pointer to the HDD
\return - None
--------------------------------------------------------------------------*/
VOS_STATUS hdd_post_voss_start_config(hdd_context_t* pHddCtx)
{
eHalStatus halStatus;
v_U32_t listenInterval;
tANI_U32 ignoreDtim;
// Send ready indication to the HDD. This will kick off the MAC
// into a 'running' state and should kick off an initial scan.
halStatus = sme_HDDReadyInd( pHddCtx->hHal );
if ( !HAL_STATUS_SUCCESS( halStatus ) )
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: sme_HDDReadyInd() failed with status "
"code %08d [x%08x]",__func__, halStatus, halStatus );
return VOS_STATUS_E_FAILURE;
}
// Set default LI and ignoreDtim into HDD context,
// otherwise under some race condition, HDD will set 0 LI value into RIVA,
// And RIVA will crash
wlan_cfgGetInt(pHddCtx->hHal, WNI_CFG_LISTEN_INTERVAL, &listenInterval);
pHddCtx->hdd_actual_LI_value = listenInterval;
wlan_cfgGetInt(pHddCtx->hHal, WNI_CFG_IGNORE_DTIM, &ignoreDtim);
pHddCtx->hdd_actual_ignore_DTIM_value = ignoreDtim;
return VOS_STATUS_SUCCESS;
}
/* wake lock APIs for HDD */
void hdd_prevent_suspend(uint32_t reason)
{
vos_wake_lock_acquire(&wlan_wake_lock, reason);
}
void hdd_allow_suspend(uint32_t reason)
{
vos_wake_lock_release(&wlan_wake_lock, reason);
}
void hdd_prevent_suspend_timeout(v_U32_t timeout, uint32_t reason)
{
vos_wake_lock_timeout_release(&wlan_wake_lock, timeout,
reason);
}
/**
* hdd_get_feature_caps_cb() - Callback invoked from WDA
* @cookie: to identify HDD request to firmware
*
* This function is invoked from WDA when feature capabilities response
* is received from firmware.
*
* Return: None
*/
static void hdd_get_feature_caps_cb(void *cookie)
{
struct hdd_request *request;
request = hdd_request_get(cookie);
if (!request) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Obsolete request"));
return;
}
pr_info("%s: Firmware feature capabilities received\n", __func__);
hdd_request_complete(request);
hdd_request_put(request);
}
/**
* hdd_get_feature_caps() - Get features supported by firmware
* @hdd_ctx: Pointer to HDD context
*
* This function uses request manager framework to get the feature
* capabilities from firmware.
*
* Return: None
*/
static void hdd_get_feature_caps(hdd_context_t *hdd_ctx)
{
VOS_STATUS status;
void *cookie;
int ret;
struct hdd_request *request;
static const struct hdd_request_params params = {
.priv_size = 0,
.timeout_ms = WLAN_WAIT_TIME_FEATURE_CAPS,
};
struct sir_feature_caps_params caps_params = {0};
request = hdd_request_alloc(&params);
if (!request) {
pr_err("%s: Request allocation failure\n", __func__);
return;
}
cookie = hdd_request_cookie(request);
caps_params.user_data = cookie;
caps_params.feature_caps_cb = hdd_get_feature_caps_cb;
status = sme_featureCapsExchange(&caps_params);
if (status != VOS_STATUS_SUCCESS) {
pr_err("%s: Unable to get feature caps\n", __func__);
goto end;
}
/* request was sent -- wait for the response */
ret = hdd_request_wait_for_response(request);
if (ret) {
pr_err("%s: SME timeout while retrieving feature caps\n",
__func__);
goto end;
}
end:
hdd_request_put(request);
}
/**---------------------------------------------------------------------------
\brief hdd_exchange_version_and_caps() - HDD function to exchange version and capability
information between Host and Riva
This function gets reported version of FW
It also finds the version of Riva headers used to compile the host
It compares the above two and prints a warning if they are different
It gets the SW and HW version string
Finally, it exchanges capabilities between host and Riva i.e. host and riva exchange a msg
indicating the features they support through a bitmap
\param - pHddCtx - Pointer to HDD context
\return - void
--------------------------------------------------------------------------*/
void hdd_exchange_version_and_caps(hdd_context_t *pHddCtx)
{
tSirVersionType versionCompiled;
tSirVersionType versionReported;
tSirVersionString versionString;
tANI_U8 fwFeatCapsMsgSupported = 0;
VOS_STATUS vstatus;
memset(&versionCompiled, 0, sizeof(versionCompiled));
memset(&versionReported, 0, sizeof(versionReported));
/* retrieve and display WCNSS version information */
do {
vstatus = sme_GetWcnssWlanCompiledVersion(pHddCtx->hHal,
&versionCompiled);
if (!VOS_IS_STATUS_SUCCESS(vstatus))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to retrieve WCNSS WLAN compiled version",
__func__);
break;
}
vstatus = sme_GetWcnssWlanReportedVersion(pHddCtx->hHal,
&versionReported);
if (!VOS_IS_STATUS_SUCCESS(vstatus))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to retrieve WCNSS WLAN reported version",
__func__);
break;
}
if ((versionCompiled.major != versionReported.major) ||
(versionCompiled.minor != versionReported.minor) ||
(versionCompiled.version != versionReported.version) ||
(versionCompiled.revision != versionReported.revision))
{
pr_err("%s: WCNSS WLAN Version %u.%u.%u.%u, "
"Host expected %u.%u.%u.%u\n",
WLAN_MODULE_NAME,
(int)versionReported.major,
(int)versionReported.minor,
(int)versionReported.version,
(int)versionReported.revision,
(int)versionCompiled.major,
(int)versionCompiled.minor,
(int)versionCompiled.version,
(int)versionCompiled.revision);
}
else
{
pr_info("%s: WCNSS WLAN version %u.%u.%u.%u\n",
WLAN_MODULE_NAME,
(int)versionReported.major,
(int)versionReported.minor,
(int)versionReported.version,
(int)versionReported.revision);
}
vstatus = sme_GetWcnssSoftwareVersion(pHddCtx->hHal,
versionString,
sizeof(versionString));
if (!VOS_IS_STATUS_SUCCESS(vstatus))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to retrieve WCNSS software version string",
__func__);
break;
}
pr_info("%s: WCNSS software version %s\n",
WLAN_MODULE_NAME, versionString);
vos_mem_copy(pHddCtx->fw_Version, versionString, sizeof(versionString));
vstatus = sme_GetWcnssHardwareVersion(pHddCtx->hHal,
versionString,
sizeof(versionString));
if (!VOS_IS_STATUS_SUCCESS(vstatus))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to retrieve WCNSS hardware version string",
__func__);
break;
}
pr_info("%s: WCNSS hardware version %s\n",
WLAN_MODULE_NAME, versionString);
/* 1.Check if FW version is greater than 0.1.1.0. Only then send host-FW capability exchange message
2.Host-FW capability exchange message is only present on riva 1.1 so
send the message only if it the riva is 1.1
minor numbers for different riva branches:
0 -> (1.0)Mainline Build
1 -> (1.1)Mainline Build
2->(1.04) Stability Build
*/
if (((versionReported.major>0) || (versionReported.minor>1) ||
((versionReported.minor>=1) && (versionReported.version>=1)))
&& ((versionReported.major == 1) && (versionReported.minor >= 1)))
fwFeatCapsMsgSupported = 1;
if (fwFeatCapsMsgSupported)
{
#ifdef WLAN_ACTIVEMODE_OFFLOAD_FEATURE
if(!pHddCtx->cfg_ini->fEnableActiveModeOffload)
sme_disableFeatureCapablity(WLANACTIVE_OFFLOAD);
#endif
/* Indicate if IBSS heartbeat monitoring needs to be offloaded */
if (!pHddCtx->cfg_ini->enableIbssHeartBeatOffload)
{
sme_disableFeatureCapablity(IBSS_HEARTBEAT_OFFLOAD);
}
hdd_get_feature_caps(pHddCtx);
}
} while (0);
}
void wlan_hdd_send_svc_nlink_msg(int type, void *data, int len)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
tAniMsgHdr *ani_hdr;
int flags = GFP_KERNEL;
void *nl_data = NULL;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), flags);
if(skb == NULL) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: alloc_skb failed", __func__);
return;
}
nlh = (struct nlmsghdr *)skb->data;
nlh->nlmsg_pid = 0; /* from kernel */
nlh->nlmsg_flags = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_type = WLAN_NL_MSG_SVC;
ani_hdr = NLMSG_DATA(nlh);
ani_hdr->type = type;
switch(type) {
case WLAN_SVC_SAP_RESTART_IND:
ani_hdr->length = 0;
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr)));
break;
case WLAN_SVC_WLAN_TP_IND:
ani_hdr->length = len;
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)
+ len));
nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr);
memcpy(nl_data, data, len);
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len));
break;
case WLAN_MSG_RPS_ENABLE_IND:
ani_hdr->length = len;
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + len));
nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr);
memcpy(nl_data, data, len);
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len));
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Attempt to send unknown nlink message %d", type);
kfree_skb(skb);
return;
}
nl_srv_bcast(skb);
return;
}
/**
* hdd_request_tcp_delack() - Find the Delack value based on RX packet
* @pHddCtx: Valid Global HDD context pointer
* @rx_packets: Number of RX packet in perticular time
*
* Based on the RX packet this function calculate next value of tcp delack.
* This function compare rx packet value to high and low threshold limit.
*
* Return: void
*/
void hdd_request_tcp_delack(hdd_context_t *pHddCtx, uint64_t rx_packets)
{
/* average of rx_packets and prev_rx is taken so that
bus width doesnot fluctuate much */
uint64_t temp_rx = (rx_packets + pHddCtx->prev_rx)/2;
enum wlan_tp_level next_rx_level = pHddCtx->cur_rx_level;
pHddCtx->prev_rx = rx_packets;
if (temp_rx > pHddCtx->cfg_ini->tcpDelAckThresholdHigh)
next_rx_level = WLAN_SVC_TP_HIGH;
else if (temp_rx <= pHddCtx->cfg_ini->tcpDelAckThresholdLow)
next_rx_level = WLAN_SVC_TP_LOW;
hdd_set_delack_value(pHddCtx, next_rx_level);
}
#define HDD_BW_GET_DIFF(x, y) ((x) >= (y) ? (x) - (y) : (ULONG_MAX - (y) + (x)))
/**
* hdd_tcp_delack_compute_function() - get link status
* @priv: Valid Global HDD context pointer
*
* This function find number of RX packet during timer life span.
* It request tcp delack with number of RX packet and re-configure delack timer
* for tcpDelAckComputeInterval timer interval.
*
* Return: void
*/
void hdd_tcp_delack_compute_function(void *priv)
{
hdd_context_t *pHddCtx = (hdd_context_t *)priv;
hdd_adapter_t *pAdapter = NULL;
v_U32_t rx_packets = 0;
hdd_adapter_list_node_t *pAdapterNode = NULL;
VOS_STATUS status = 0;
for (status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
NULL != pAdapterNode && VOS_STATUS_SUCCESS == status;
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pAdapterNode)) {
if ((pAdapter = pAdapterNode->pAdapter) == NULL)
continue;
rx_packets += HDD_BW_GET_DIFF(pAdapter->stats.rx_packets,
pAdapter->prev_rx_packets);
pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
}
hdd_request_tcp_delack(pHddCtx, rx_packets);
vos_timer_start(&pHddCtx->delack_timer,
pHddCtx->cfg_ini->tcpDelAckComputeInterval);
}
/**---------------------------------------------------------------------------
\brief hdd_is_5g_supported() - HDD function to know if hardware supports 5GHz
\param - pHddCtx - Pointer to the hdd context
\return - true if hardware supports 5GHz
--------------------------------------------------------------------------*/
boolean hdd_is_5g_supported(hdd_context_t * pHddCtx)
{
/* If wcnss_wlan_iris_xo_mode() returns WCNSS_XO_48MHZ(1);
* then hardware support 5Ghz.
*/
if (WCNSS_XO_48MHZ == wcnss_wlan_iris_xo_mode())
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Hardware supports 5Ghz", __func__);
return true;
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Hardware doesn't supports 5Ghz",
__func__);
return false;
}
}
/**---------------------------------------------------------------------------
\brief hdd_generate_iface_mac_addr_auto() - HDD Mac Interface Auto
generate function
This is generate the random mac address for WLAN interface
\param - pHddCtx - Pointer to HDD context
idx - Start interface index to get auto
generated mac addr.
mac_addr - Mac address
\return - 0 for success, < 0 for failure
--------------------------------------------------------------------------*/
static int hdd_generate_iface_mac_addr_auto(hdd_context_t *pHddCtx,
int idx, v_MACADDR_t mac_addr)
{
int i;
unsigned int serialno;
serialno = wcnss_get_serial_number();
if (0 != serialno)
{
/* MAC address has 3 bytes of OUI so we have a maximum of 3
bytes of the serial number that can be used to generate
the other 3 bytes of the MAC address. Mask off all but
the lower 3 bytes (this will also make sure we don't
overflow in the next step) */
serialno &= 0x00FFFFFF;
/* we need a unique address for each session */
serialno *= VOS_MAX_CONCURRENCY_PERSONA;
/* autogen other Mac addresses */
for (i = idx; i < VOS_MAX_CONCURRENCY_PERSONA; i++)
{
/* start with the entire default address */
pHddCtx->cfg_ini->intfMacAddr[i] = mac_addr;
/* then replace the lower 3 bytes */
pHddCtx->cfg_ini->intfMacAddr[i].bytes[3] = (serialno >> 16) & 0xFF;
pHddCtx->cfg_ini->intfMacAddr[i].bytes[4] = (serialno >> 8) & 0xFF;
pHddCtx->cfg_ini->intfMacAddr[i].bytes[5] = serialno & 0xFF;
if (0 == memcmp(&pHddCtx->cfg_ini->intfMacAddr[i].bytes[0],
&mac_addr.bytes[0], VOS_MAC_ADDR_SIZE))
pHddCtx->cfg_ini->intfMacAddr[i].bytes[5] +=
VOS_MAX_CONCURRENCY_PERSONA;
serialno++;
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Derived Mac Addr: "
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[i].bytes));
}
}
else
{
hddLog(LOGE, FL("Failed to Get Serial NO"));
return -1;
}
return 0;
}
int wlan_hdd_mon_open(hdd_context_t *pHddCtx)
{
VOS_STATUS status;
v_CONTEXT_t pVosContext= NULL;
hdd_adapter_t *pAdapter= NULL;
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if (NULL == pVosContext)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Trying to open VOSS without a PreOpen", __func__);
VOS_ASSERT(0);
return VOS_STATUS_E_FAILURE;
}
status = vos_nv_open();
if (!VOS_IS_STATUS_SUCCESS(status))
{
/* NV module cannot be initialized */
hddLog( VOS_TRACE_LEVEL_FATAL,
"%s: vos_nv_open failed", __func__);
return VOS_STATUS_E_FAILURE;
}
status = vos_init_wiphy_from_nv_bin();
if (!VOS_IS_STATUS_SUCCESS(status))
{
/* NV module cannot be initialized */
hddLog( VOS_TRACE_LEVEL_FATAL,
"%s: vos_init_wiphy failed", __func__);
goto err_vos_nv_close;
}
status = vos_open( &pVosContext, pHddCtx->parent_dev);
if ( !VOS_IS_STATUS_SUCCESS( status ))
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_open failed", __func__);
goto err_vos_nv_close;
}
status = vos_mon_start( pVosContext );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__);
goto err_vosclose;
}
WLANTL_SetMonRxCbk( pVosContext, hdd_rx_packet_monitor_cbk );
sme_featureCapsExchange(NULL);
wcnss_wlan_set_drvdata(pHddCtx->parent_dev, pHddCtx);
pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_MONITOR, "wlan%d",
wlan_hdd_get_intf_addr(pHddCtx), FALSE );
if( pAdapter == NULL )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", __func__);
goto err_close_adapter;
}
//Initialize the nlink service
if(nl_srv_init() != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: nl_srv_init failed", __func__);
goto err_close_adapter;
}
return VOS_STATUS_SUCCESS;
err_close_adapter:
hdd_close_all_adapters( pHddCtx );
vos_mon_stop( pVosContext );
err_vosclose:
status = vos_sched_close( pVosContext );
if (!VOS_IS_STATUS_SUCCESS(status)) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to close VOSS Scheduler", __func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ) );
}
vos_close(pVosContext );
err_vos_nv_close:
vos_nv_close();
return status;
}
/**---------------------------------------------------------------------------
\brief hdd_11d_scan_done - callback to be executed when 11d scan is
completed to flush out the scan results
11d scan is done during driver load and is a passive scan on all
channels supported by the device, 11d scans may find some APs on
frequencies which are forbidden to be used in the regulatory domain
the device is operating in. If these APs are notified to the supplicant
it may try to connect to these APs, thus flush out all the scan results
which are present in SME after 11d scan is done.
\return - eHalStatus
--------------------------------------------------------------------------*/
static eHalStatus hdd_11d_scan_done(tHalHandle halHandle, void *pContext,
tANI_U32 scanId, eCsrScanStatus status)
{
ENTER();
sme_ScanFlushResult(halHandle, 0);
EXIT();
return eHAL_STATUS_SUCCESS;
}
/**---------------------------------------------------------------------------
\brief hdd_init_frame_logging_done - callback to be executed when mgmt frame
logging is completed successfully.
\return - None
--------------------------------------------------------------------------*/
void hdd_init_frame_logging_done(void *fwlogInitCbContext, tAniLoggingInitRsp *pRsp)
{
hdd_context_t* pHddCtx = (hdd_context_t*)fwlogInitCbContext;
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is NULL",__func__);
return;
}
if ((pRsp->status == VOS_STATUS_SUCCESS) &&
(TRUE == pHddCtx->cfg_ini->enableMgmtLogging))
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Mgmt Frame Logging init successful"));
pHddCtx->mgmt_frame_logging = TRUE;
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Mgmt Frame Logging init not success"));
pHddCtx->mgmt_frame_logging = FALSE;
return;
}
/*Check feature supported by FW*/
if(TRUE == sme_IsFeatureSupportedByFW(MEMORY_DUMP_SUPPORTED))
{
//Store fwr mem dump size given by firmware.
wlan_store_fwr_mem_dump_size(pRsp->fw_mem_dump_max_size);
}
else
{
wlan_store_fwr_mem_dump_size(0);
}
}
/**---------------------------------------------------------------------------
\brief hdd_init_frame_logging - function to initialize frame logging.
Currently only Mgmt Frames are logged in both TX
and Rx direction and are sent to userspace
application using logger thread when queried.
\return - None
--------------------------------------------------------------------------*/
void hdd_init_frame_logging(hdd_context_t* pHddCtx)
{
eHalStatus halStatus = eHAL_STATUS_FAILURE;
tSirFWLoggingInitParam wlanFWLoggingInitParam = {0};
if (TRUE != sme_IsFeatureSupportedByFW(MGMT_FRAME_LOGGING) &&
TRUE != sme_IsFeatureSupportedByFW(LOGGING_ENHANCEMENT))
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("MGMT_FRAME_LOGGING not supp by FW"));
return;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Configuring %s %s %s Logging",__func__,
pHddCtx->cfg_ini->enableFWLogging?"FW Log,":"",
pHddCtx->cfg_ini->enableContFWLogging ? "Cont FW log,":"",
pHddCtx->cfg_ini->enableMgmtLogging ? "Mgmt Pkt Log":"");
if (pHddCtx->cfg_ini->enableFWLogging ||
pHddCtx->cfg_ini->enableContFWLogging)
{
wlanFWLoggingInitParam.enableFlag |= WLAN_QXDM_LOG_EN;
}
if (pHddCtx->cfg_ini->enableMgmtLogging)
{
wlanFWLoggingInitParam.enableFlag |= WLAN_FRAME_LOG_EN;
}
if (pHddCtx->cfg_ini->enableBMUHWtracing)
{
wlanFWLoggingInitParam.enableFlag |= WLAN_BMUHW_TRACE_LOG_EN;
}
if( wlanFWLoggingInitParam.enableFlag == 0 )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Logging not enabled", __func__);
return;
}
wlanFWLoggingInitParam.frameType = WLAN_FRAME_LOGGING_FRAMETYPE_MGMT;
wlanFWLoggingInitParam.frameSize = WLAN_MGMT_LOGGING_FRAMESIZE_128BYTES;
wlanFWLoggingInitParam.bufferMode = WLAN_FRAME_LOGGING_BUFFERMODE_CIRCULAR;
wlanFWLoggingInitParam.continuousFrameLogging =
pHddCtx->cfg_ini->enableContFWLogging;
wlanFWLoggingInitParam.enableFlag &= ~WLAN_DPU_TXP_LOG_EN;
wlanFWLoggingInitParam.minLogBufferSize =
pHddCtx->cfg_ini->minLoggingBufferSize;
wlanFWLoggingInitParam.maxLogBufferSize =
pHddCtx->cfg_ini->maxLoggingBufferSize;
wlanFWLoggingInitParam.fwlogInitCallback = hdd_init_frame_logging_done;
wlanFWLoggingInitParam.fwlogInitCbContext= pHddCtx;
halStatus = sme_InitMgmtFrameLogging(pHddCtx->hHal, &wlanFWLoggingInitParam);
if (eHAL_STATUS_SUCCESS != halStatus)
{
hddLog(LOGE, FL("sme_InitMgmtFrameLogging failed, returned %d"),
halStatus);
}
return;
}
static void hdd_dp_util_send_rps_ind(hdd_context_t *hdd_ctxt)
{
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *adapter_node, *next;
VOS_STATUS status = VOS_STATUS_SUCCESS;
struct wlan_rps_data rps_data;
int count;
if(!hdd_ctxt->cfg_ini->rps_mask)
{
return;
}
for (count=0; count < WLAN_SVC_IFACE_NUM_QUEUES; count++)
{
rps_data.cpu_map[count] = hdd_ctxt->cfg_ini->rps_mask;
}
rps_data.num_queues = WLAN_SVC_IFACE_NUM_QUEUES;
hddLog(LOG1, FL("0x%x 0x%x 0x%x 0x%x 0x%x 0x%x"),
rps_data.cpu_map[0], rps_data.cpu_map[1],rps_data.cpu_map[2],
rps_data.cpu_map[3], rps_data.cpu_map[4], rps_data.cpu_map[5]);
status = hdd_get_front_adapter (hdd_ctxt, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status)
{
adapter = adapter_node->pAdapter;
if (NULL != adapter) {
strlcpy(rps_data.ifname, adapter->dev->name,
sizeof(rps_data.ifname));
wlan_hdd_send_svc_nlink_msg(WLAN_MSG_RPS_ENABLE_IND,
(void *)&rps_data,sizeof(rps_data));
}
status = hdd_get_next_adapter (hdd_ctxt, adapter_node, &next);
adapter_node = next;
}
}
void wlan_hdd_schedule_defer_scan(struct work_struct *work)
{
scan_context_t *scan_ctx =
container_of(work, scan_context_t, scan_work.work);
if (NULL == scan_ctx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("scan_ctx is NULL"));
return;
}
if (unlikely(TDLS_CTX_MAGIC != scan_ctx->magic))
return;
scan_ctx->attempt++;
wlan_hdd_cfg80211_scan(scan_ctx->wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
scan_ctx->dev,
#endif
scan_ctx->scan_request);
}
int wlan_hdd_copy_defer_scan_context(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
scan_context_t *scan_ctx;
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
{
return -1;
}
scan_ctx = &pHddCtx->scan_ctxt;
scan_ctx->wiphy = wiphy;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
scan_ctx->dev = dev;
#endif
scan_ctx->scan_request = request;
EXIT();
return 0;
}
void wlan_hdd_defer_scan_init_work(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
struct net_device *dev,
#endif
struct cfg80211_scan_request *request,
unsigned long delay)
{
if (TDLS_CTX_MAGIC != pHddCtx->scan_ctxt.magic)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0))
wlan_hdd_copy_defer_scan_context(pHddCtx, wiphy, dev, request);
#else
wlan_hdd_copy_defer_scan_context(pHddCtx, wiphy, request);
#endif
pHddCtx->scan_ctxt.attempt = 0;
pHddCtx->scan_ctxt.magic = TDLS_CTX_MAGIC;
}
schedule_delayed_work(&pHddCtx->scan_ctxt.scan_work, delay);
}
void wlan_hdd_init_deinit_defer_scan_context(scan_context_t *scan_ctx)
{
scan_ctx->magic = 0;
scan_ctx->attempt = 0;
scan_ctx->reject = 0;
scan_ctx->scan_request = NULL;
return;
}
/**---------------------------------------------------------------------------
\brief hdd_wlan_startup() - HDD init function
This is the driver startup code executed once a WLAN device has been detected
\param - dev - Pointer to the underlying device
\return - 0 for success, < 0 for failure
--------------------------------------------------------------------------*/
int hdd_wlan_startup(struct device *dev )
{
VOS_STATUS status;
hdd_adapter_t *pAdapter = NULL;
hdd_adapter_t *pP2pAdapter = NULL;
hdd_adapter_t *softapAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext= NULL;
#ifdef WLAN_BTAMP_FEATURE
VOS_STATUS vStatus = VOS_STATUS_SUCCESS;
WLANBAP_ConfigType btAmpConfig;
hdd_config_t *pConfig;
#endif
int ret;
struct wiphy *wiphy;
v_MACADDR_t mac_addr;
ENTER();
/*
* cfg80211: wiphy allocation
*/
wiphy = wlan_hdd_cfg80211_wiphy_alloc(sizeof(hdd_context_t)) ;
if(wiphy == NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: cfg80211 init failed", __func__);
return -EIO;
}
pHddCtx = wiphy_priv(wiphy);
//Initialize the adapter context to zeros.
vos_mem_zero(pHddCtx, sizeof( hdd_context_t ));
pHddCtx->wiphy = wiphy;
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_LOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
/* register for riva power on lock to platform driver
* Locking power early to ensure FW doesn't reset by kernel while
* host driver is busy initializing itself */
if (req_riva_power_on_lock("wlan"))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: req riva power on lock failed",
__func__);
goto err_free_hdd_context;
}
/*Get vos context here bcoz vos_open requires it*/
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if(pVosContext == NULL)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed vos_get_global_context",__func__);
goto err_free_hdd_context;
}
//Save the Global VOSS context in adapter context for future.
pHddCtx->pvosContext = pVosContext;
//Save the adapter context in global context for future.
((VosContextType*)(pVosContext))->pHDDContext = (v_VOID_t*)pHddCtx;
pHddCtx->parent_dev = dev;
pHddCtx->last_scan_reject_session_id = 0xFF;
pHddCtx->last_scan_reject_reason = 0;
pHddCtx->last_scan_reject_timestamp = 0;
pHddCtx->scan_reject_cnt = 0;
init_completion(&pHddCtx->full_pwr_comp_var);
init_completion(&pHddCtx->standby_comp_var);
init_completion(&pHddCtx->req_bmps_comp_var);
init_completion(&pHddCtx->scan_info.scan_req_completion_event);
init_completion(&pHddCtx->scan_info.abortscan_event_var);
init_completion(&pHddCtx->wiphy_channel_update_event);
init_completion(&pHddCtx->ssr_comp_var);
init_completion(&pHddCtx->mc_sus_event_var);
init_completion(&pHddCtx->tx_sus_event_var);
init_completion(&pHddCtx->rx_sus_event_var);
hdd_init_ll_stats_ctx(pHddCtx);
hdd_init_nud_stats_ctx(pHddCtx);
#ifdef CONFIG_ENABLE_LINUX_REG
init_completion(&pHddCtx->linux_reg_req);
#else
init_completion(&pHddCtx->driver_crda_req);
#endif
#ifdef WLAN_FEATURE_EXTSCAN
init_completion(&pHddCtx->ext_scan_context.response_event);
#endif /* WLAN_FEATURE_EXTSCAN */
spin_lock_init(&pHddCtx->schedScan_lock);
vos_spin_lock_init(&pHddCtx->sap_update_info_lock);
hdd_list_init( &pHddCtx->hddAdapters, MAX_NUMBER_OF_ADAPTERS );
vos_init_delayed_work(&pHddCtx->spoof_mac_addr_work,
hdd_processSpoofMacAddrRequest);
vos_init_work(&pHddCtx->sap_start_work, hdd_sap_restart_handle);
vos_init_delayed_work(&pHddCtx->ecsa_chan_change_work,
hdd_force_scc_with_ecsa_handle);
#ifdef FEATURE_WLAN_TDLS
/* tdls_lock is initialized before an hdd_open_adapter ( which is
* invoked by other instances also) to protect the concurrent
* access for the Adapters by TDLS module.
*/
mutex_init(&pHddCtx->tdls_lock);
#endif
mutex_init(&pHddCtx->spoofMacAddr.macSpoofingLock);
mutex_init(&pHddCtx->wmmLock);
hdd_init_offloaded_packets_ctx(pHddCtx);
/* By default Strict Regulatory For FCC should be false */
pHddCtx->nEnableStrictRegulatoryForFCC = FALSE;
// Load all config first as TL config is needed during vos_open
pHddCtx->cfg_ini = (hdd_config_t*) kmalloc(sizeof(hdd_config_t), GFP_KERNEL);
if(pHddCtx->cfg_ini == NULL)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed kmalloc hdd_config_t",__func__);
goto err_free_hdd_context;
}
hdd_request_manager_init();
vos_mem_zero(pHddCtx->cfg_ini, sizeof( hdd_config_t ));
// Read and parse the qcom_cfg.ini file
status = hdd_parse_config_ini( pHddCtx );
if ( VOS_STATUS_SUCCESS != status )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: error parsing %s",
__func__, WLAN_INI_FILE);
goto err_config;
}
#ifdef MEMORY_DEBUG
if (pHddCtx->cfg_ini->IsMemoryDebugSupportEnabled)
vos_mem_init();
hddLog(VOS_TRACE_LEVEL_INFO, "%s: gEnableMemoryDebug=%d",
__func__, pHddCtx->cfg_ini->IsMemoryDebugSupportEnabled);
#endif
/* INI has been read, initialise the configuredMcastBcastFilter with
* INI value as this will serve as the default value
*/
pHddCtx->configuredMcastBcastFilter = pHddCtx->cfg_ini->mcastBcastFilterSetting;
hddLog(VOS_TRACE_LEVEL_INFO, "Setting configuredMcastBcastFilter: %d",
pHddCtx->cfg_ini->mcastBcastFilterSetting);
if (false == hdd_is_5g_supported(pHddCtx))
{
//5Ghz is not supported.
if (1 != pHddCtx->cfg_ini->nBandCapability)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Setting pHddCtx->cfg_ini->nBandCapability = 1", __func__);
pHddCtx->cfg_ini->nBandCapability = 1;
}
}
/* If SNR Monitoring is enabled, FW has to parse all beacons
* for calcaluting and storing the average SNR, so set Nth beacon
* filter to 1 to enable FW to parse all the beaocons
*/
if (1 == pHddCtx->cfg_ini->fEnableSNRMonitoring)
{
/* The log level is deliberately set to WARN as overriding
* nthBeaconFilter to 1 will increase power cosumption and this
* might just prove helpful to detect the power issue.
*/
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Setting pHddCtx->cfg_ini->nthBeaconFilter = 1", __func__);
pHddCtx->cfg_ini->nthBeaconFilter = 1;
}
/*
* cfg80211: Initialization ...
*/
if (0 < wlan_hdd_cfg80211_init(dev, wiphy, pHddCtx->cfg_ini))
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: wlan_hdd_cfg80211_init return failure", __func__);
goto err_config;
}
// Update VOS trace levels based upon the cfg.ini
hdd_vos_trace_enable(VOS_MODULE_ID_BAP,
pHddCtx->cfg_ini->vosTraceEnableBAP);
hdd_vos_trace_enable(VOS_MODULE_ID_TL,
pHddCtx->cfg_ini->vosTraceEnableTL);
hdd_vos_trace_enable(VOS_MODULE_ID_WDI,
pHddCtx->cfg_ini->vosTraceEnableWDI);
hdd_vos_trace_enable(VOS_MODULE_ID_HDD,
pHddCtx->cfg_ini->vosTraceEnableHDD);
hdd_vos_trace_enable(VOS_MODULE_ID_SME,
pHddCtx->cfg_ini->vosTraceEnableSME);
hdd_vos_trace_enable(VOS_MODULE_ID_PE,
pHddCtx->cfg_ini->vosTraceEnablePE);
hdd_vos_trace_enable(VOS_MODULE_ID_PMC,
pHddCtx->cfg_ini->vosTraceEnablePMC);
hdd_vos_trace_enable(VOS_MODULE_ID_WDA,
pHddCtx->cfg_ini->vosTraceEnableWDA);
hdd_vos_trace_enable(VOS_MODULE_ID_SYS,
pHddCtx->cfg_ini->vosTraceEnableSYS);
hdd_vos_trace_enable(VOS_MODULE_ID_VOSS,
pHddCtx->cfg_ini->vosTraceEnableVOSS);
hdd_vos_trace_enable(VOS_MODULE_ID_SAP,
pHddCtx->cfg_ini->vosTraceEnableSAP);
hdd_vos_trace_enable(VOS_MODULE_ID_HDD_SOFTAP,
pHddCtx->cfg_ini->vosTraceEnableHDDSAP);
// Update WDI trace levels based upon the cfg.ini
hdd_wdi_trace_enable(eWLAN_MODULE_DAL,
pHddCtx->cfg_ini->wdiTraceEnableDAL);
hdd_wdi_trace_enable(eWLAN_MODULE_DAL_CTRL,
pHddCtx->cfg_ini->wdiTraceEnableCTL);
hdd_wdi_trace_enable(eWLAN_MODULE_DAL_DATA,
pHddCtx->cfg_ini->wdiTraceEnableDAT);
hdd_wdi_trace_enable(eWLAN_MODULE_PAL,
pHddCtx->cfg_ini->wdiTraceEnablePAL);
if (VOS_FTM_MODE == hdd_get_conparam())
{
if ( VOS_STATUS_SUCCESS != wlan_hdd_ftm_open(pHddCtx) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: wlan_hdd_ftm_open Failed",__func__);
goto err_free_hdd_context;
}
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: FTM driver loaded success fully",__func__);
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_NO_LOAD_UNLOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE);
return VOS_STATUS_SUCCESS;
}
if( VOS_MONITOR_MODE == hdd_get_conparam())
{
if ( VOS_STATUS_SUCCESS != wlan_hdd_mon_open(pHddCtx))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: wlan_hdd_mon_open Failed",__func__);
goto err_free_hdd_context;
}
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Driver loaded in Monitor Mode",__func__);
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_NO_LOAD_UNLOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE);
return VOS_STATUS_SUCCESS;
}
//Open watchdog module
if(pHddCtx->cfg_ini->fIsLogpEnabled)
{
status = vos_watchdog_open(pVosContext,
&((VosContextType*)pVosContext)->vosWatchdog, sizeof(VosWatchdogContext));
if(!VOS_IS_STATUS_SUCCESS( status ))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_watchdog_open failed",__func__);
goto err_config;
}
}
pHddCtx->isLogpInProgress = FALSE;
vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, FALSE);
#ifdef CONFIG_ENABLE_LINUX_REG
/* initialize the NV module. This is required so that
we can initialize the channel information in wiphy
from the NV.bin data. The channel information in
wiphy needs to be initialized before wiphy registration */
status = vos_nv_open();
if (!VOS_IS_STATUS_SUCCESS(status))
{
/* NV module cannot be initialized */
hddLog( VOS_TRACE_LEVEL_FATAL,
"%s: vos_nv_open failed", __func__);
goto err_wdclose;
}
status = vos_init_wiphy_from_nv_bin();
if (!VOS_IS_STATUS_SUCCESS(status))
{
/* NV module cannot be initialized */
hddLog( VOS_TRACE_LEVEL_FATAL,
"%s: vos_init_wiphy failed", __func__);
goto err_vos_nv_close;
}
#endif
//Initialize the nlink service
if(nl_srv_init() != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: nl_srv_init failed", __func__);
goto err_vos_nv_close;
}
#ifdef WLAN_KD_READY_NOTIFIER
pHddCtx->kd_nl_init = 1;
#endif /* WLAN_KD_READY_NOTIFIER */
vos_set_roam_delay_stats_enabled(pHddCtx->cfg_ini->gEnableRoamDelayStats);
status = vos_open( &pVosContext, pHddCtx->parent_dev);
if ( !VOS_IS_STATUS_SUCCESS( status ))
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_open failed", __func__);
goto err_nl_srv;
}
pHddCtx->hHal = (tHalHandle)vos_get_context( VOS_MODULE_ID_SME, pVosContext );
if ( NULL == pHddCtx->hHal )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: HAL context is null", __func__);
goto err_vosclose;
}
status = vos_preStart( pHddCtx->pvosContext );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_preStart failed", __func__);
goto err_vosclose;
}
if (0 == enable_dfs_chan_scan || 1 == enable_dfs_chan_scan)
{
pHddCtx->cfg_ini->enableDFSChnlScan = enable_dfs_chan_scan;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: module enable_dfs_chan_scan set to %d",
__func__, enable_dfs_chan_scan);
}
if (0 == enable_11d || 1 == enable_11d)
{
pHddCtx->cfg_ini->Is11dSupportEnabled = enable_11d;
hddLog(VOS_TRACE_LEVEL_INFO, "%s: module enable_11d set to %d",
__func__, enable_11d);
}
/* Note that the vos_preStart() sequence triggers the cfg download.
The cfg download must occur before we update the SME config
since the SME config operation must access the cfg database */
status = hdd_set_sme_config( pHddCtx );
if ( VOS_STATUS_SUCCESS != status )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Failed hdd_set_sme_config", __func__);
goto err_vosclose;
}
/* In the integrated architecture we update the configuration from
the INI file and from NV before vOSS has been started so that
the final contents are available to send down to the cCPU */
// Apply the cfg.ini to cfg.dat
if (FALSE == hdd_update_config_dat(pHddCtx))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: config update failed",__func__ );
goto err_vosclose;
}
// Get mac addr from platform driver
ret = wcnss_get_wlan_mac_address((char*)&mac_addr.bytes);
if ((0 == ret) && (!vos_is_macaddr_zero(&mac_addr)))
{
/* Store the mac addr for first interface */
pHddCtx->cfg_ini->intfMacAddr[0] = mac_addr;
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WLAN Mac Addr: "
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[0].bytes));
/* Here, passing Arg2 as 1 because we do not want to change the
last 3 bytes (means non OUI bytes) of first interface mac
addr.
*/
if (0 != hdd_generate_iface_mac_addr_auto(pHddCtx, 1, mac_addr))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to generate wlan interface mac addr "
"using MAC from ini file ", __func__);
}
}
else if (VOS_STATUS_SUCCESS != hdd_update_config_from_nv(pHddCtx))
{
// Apply the NV to cfg.dat
/* Prima Update MAC address only at here */
#ifdef WLAN_AUTOGEN_MACADDR_FEATURE
/* There was not a valid set of MAC Addresses in NV. See if the
default addresses were modified by the cfg.ini settings. If so,
we'll use them, but if not, we'll autogenerate a set of MAC
addresses based upon the device serial number */
static const v_MACADDR_t default_address =
{{0x00, 0x0A, 0xF5, 0x89, 0x89, 0xFF}};
if (0 == memcmp(&default_address, &pHddCtx->cfg_ini->intfMacAddr[0],
sizeof(default_address)))
{
/* cfg.ini has the default address, invoke autogen logic */
/* Here, passing Arg2 as 0 because we want to change the
last 3 bytes (means non OUI bytes) of all the interfaces
mac addr.
*/
if (0 != hdd_generate_iface_mac_addr_auto(pHddCtx, 0,
default_address))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to generate wlan interface mac addr "
"using MAC from ini file " MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[0].bytes));
}
}
else
#endif //WLAN_AUTOGEN_MACADDR_FEATURE
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid MAC address in NV, using MAC from ini file "
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(pHddCtx->cfg_ini->intfMacAddr[0].bytes));
}
}
{
eHalStatus halStatus;
/* Set the MAC Address Currently this is used by HAL to
* add self sta. Remove this once self sta is added as
* part of session open.
*/
halStatus = cfgSetStr( pHddCtx->hHal, WNI_CFG_STA_ID,
(v_U8_t *)&pHddCtx->cfg_ini->intfMacAddr[0],
sizeof( pHddCtx->cfg_ini->intfMacAddr[0]) );
if (!HAL_STATUS_SUCCESS( halStatus ))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Failed to set MAC Address. "
"HALStatus is %08d [x%08x]",__func__, halStatus, halStatus );
goto err_vosclose;
}
}
/*Start VOSS which starts up the SME/MAC/HAL modules and everything else
Note: Firmware image will be read and downloaded inside vos_start API */
status = vos_start( pHddCtx->pvosContext );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__);
if (isSsrPanicOnFailure())
VOS_BUG(0);
goto err_vosclose;
}
#ifdef FEATURE_WLAN_CH_AVOID
/* Plug in avoid channel notification callback
* This should happen before ADD_SELF_STA
* FW will send first IND with ADD_SELF_STA REQ from host */
/* check the Channel Avoidance is enabled */
if (TRUE == pHddCtx->cfg_ini->fenableCHAvoidance)
{
sme_AddChAvoidCallback(pHddCtx->hHal,
hdd_hostapd_ch_avoid_cb);
}
#endif /* FEATURE_WLAN_CH_AVOID */
/* Exchange capability info between Host and FW and also get versioning info from FW */
hdd_exchange_version_and_caps(pHddCtx);
#ifdef CONFIG_ENABLE_LINUX_REG
status = wlan_hdd_init_channels(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: wlan_hdd_init_channels failed",
__func__);
goto err_vosstop;
}
#endif
status = hdd_post_voss_start_config( pHddCtx );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_post_voss_start_config failed",
__func__);
goto err_vosstop;
}
wlan_hdd_cfg80211_scan_randomization_init(wiphy);
#ifndef CONFIG_ENABLE_LINUX_REG
wlan_hdd_cfg80211_update_reg_info( wiphy );
/* registration of wiphy dev with cfg80211 */
if (0 > wlan_hdd_cfg80211_register(wiphy))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__);
goto err_vosstop;
}
#endif
#ifdef CONFIG_ENABLE_LINUX_REG
/* registration of wiphy dev with cfg80211 */
if (0 > wlan_hdd_cfg80211_register(wiphy))
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__);
goto err_vosstop;
}
status = wlan_hdd_init_channels_for_cc(pHddCtx, INIT);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: wlan_hdd_init_channels_for_cc failed",
__func__);
goto err_unregister_wiphy;
}
#endif
wcnss_wlan_set_drvdata(pHddCtx->parent_dev, pHddCtx);
#ifdef SAP_AUTH_OFFLOAD
if (!sme_IsFeatureSupportedByFW(SAP_OFFLOADS))
{
hddLog(VOS_TRACE_LEVEL_INFO, FL(" SAP AUTH OFFLOAD not supp by FW"));
pHddCtx->cfg_ini->enable_sap_auth_offload = 0;
}
#endif
if (VOS_STA_SAP_MODE == hdd_get_conparam())
{
pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_SOFTAP, "softap.%d",
wlan_hdd_get_intf_addr(pHddCtx), FALSE );
}
else
{
pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_INFRA_STATION, "wlan%d",
wlan_hdd_get_intf_addr(pHddCtx), FALSE );
if (pAdapter != NULL)
{
if (pHddCtx->cfg_ini->isP2pDeviceAddrAdministrated && !(pHddCtx->cfg_ini->intfMacAddr[0].bytes[0] & 0x02))
{
vos_mem_copy( pHddCtx->p2pDeviceAddress.bytes,
pHddCtx->cfg_ini->intfMacAddr[0].bytes,
sizeof(tSirMacAddr));
/* Generate the P2P Device Address. This consists of the device's
* primary MAC address with the locally administered bit set.
*/
pHddCtx->p2pDeviceAddress.bytes[0] |= 0x02;
}
else
{
tANI_U8* p2p_dev_addr = wlan_hdd_get_intf_addr(pHddCtx);
if (p2p_dev_addr != NULL)
{
vos_mem_copy(&pHddCtx->p2pDeviceAddress.bytes[0],
p2p_dev_addr, VOS_MAC_ADDR_SIZE);
}
else
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to allocate mac_address for p2p_device",
__func__);
goto err_close_adapter;
}
}
pP2pAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_P2P_DEVICE, "p2p%d",
&pHddCtx->p2pDeviceAddress.bytes[0], FALSE );
if ( NULL == pP2pAdapter )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to do hdd_open_adapter for P2P Device Interface",
__func__);
goto err_close_adapter;
}
}
}
if( pAdapter == NULL )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", __func__);
goto err_close_adapter;
}
if ((strlen(pHddCtx->cfg_ini->enabledefaultSAP) != 0) &&
(strcmp(pHddCtx->cfg_ini->enabledefaultSAP, "") != 0)) {
softapAdapter = hdd_open_adapter( pHddCtx, WLAN_HDD_SOFTAP,
pHddCtx->cfg_ini->enabledefaultSAP,
wlan_hdd_get_intf_addr(pHddCtx), FALSE);
if (!softapAdapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", __func__);
goto err_close_adapter;
}
}
if (country_code)
{
eHalStatus ret;
INIT_COMPLETION(pAdapter->change_country_code);
hdd_checkandupdate_dfssetting(pAdapter, country_code);
#ifndef CONFIG_ENABLE_LINUX_REG
hdd_checkandupdate_phymode(pAdapter, country_code);
#endif
ret = sme_ChangeCountryCode(pHddCtx->hHal,
(void *)(tSmeChangeCountryCallback)
wlan_hdd_change_country_code_callback,
country_code,
pAdapter, pHddCtx->pvosContext,
eSIR_TRUE, eSIR_TRUE);
if (eHAL_STATUS_SUCCESS == ret)
{
ret = wait_for_completion_interruptible_timeout(
&pAdapter->change_country_code,
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (0 >= ret)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: SME while setting country code timed out", __func__);
}
}
else
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: SME Change Country code from module param fail ret=%d",
__func__, ret);
}
}
#ifdef WLAN_BTAMP_FEATURE
vStatus = WLANBAP_Open(pVosContext);
if(!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to open BAP",__func__);
goto err_close_adapter;
}
vStatus = BSL_Init(pVosContext);
if(!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to Init BSL",__func__);
goto err_bap_close;
}
vStatus = WLANBAP_Start(pVosContext);
if (!VOS_IS_STATUS_SUCCESS(vStatus))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to start TL",__func__);
goto err_bap_close;
}
pConfig = pHddCtx->cfg_ini;
btAmpConfig.ucPreferredChannel = pConfig->preferredChannel;
status = WLANBAP_SetConfig(&btAmpConfig);
#endif //WLAN_BTAMP_FEATURE
/*
* UapsdMask is 0xf if U-APSD is enbaled for all AC's...
* The value of CFG_QOS_WMM_UAPSD_MASK_DEFAULT is 0xaa(Magic Value)
* which is greater than 0xf. So the below check is safe to make
* sure that there is no entry for UapsdMask in the ini
*/
if (CFG_QOS_WMM_UAPSD_MASK_DEFAULT == pHddCtx->cfg_ini->UapsdMask)
{
if(IS_DYNAMIC_WMM_PS_ENABLED)
{
hddLog(VOS_TRACE_LEVEL_DEBUG,"%s: Enable UAPSD for VI & VO",
__func__);
pHddCtx->cfg_ini->UapsdMask =
CFG_QOS_WMM_UAPSD_MASK_DYMANIC_WMM_PS_DEFAULT;
}
else
{
hddLog(VOS_TRACE_LEVEL_DEBUG,"%s: Do not enable UAPSD",
__func__);
pHddCtx->cfg_ini->UapsdMask =
CFG_QOS_WMM_UAPSD_MASK_LEGACY_WMM_PS_DEFAULT;
}
}
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
if(!(IS_ROAM_SCAN_OFFLOAD_FEATURE_ENABLE))
{
hddLog(VOS_TRACE_LEVEL_DEBUG,"%s: ROAM_SCAN_OFFLOAD Feature not supported",__func__);
pHddCtx->cfg_ini->isRoamOffloadScanEnabled = 0;
sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal),
pHddCtx->cfg_ini->isRoamOffloadScanEnabled);
}
#endif
wlan_hdd_tdls_init(pHddCtx);
wlan_hdd_init_deinit_defer_scan_context(&pHddCtx->scan_ctxt);
vos_init_delayed_work(&pHddCtx->scan_ctxt.scan_work,
wlan_hdd_schedule_defer_scan);
sme_Register11dScanDoneCallback(pHddCtx->hHal, hdd_11d_scan_done);
/* Register with platform driver as client for Suspend/Resume */
status = hddRegisterPmOps(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddRegisterPmOps failed",__func__);
#ifdef WLAN_BTAMP_FEATURE
goto err_bap_stop;
#else
goto err_close_adapter;
#endif //WLAN_BTAMP_FEATURE
}
/* Open debugfs interface */
if (VOS_STATUS_SUCCESS != hdd_debugfs_init(pAdapter))
{
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: hdd_debugfs_init failed!", __func__);
}
/* Register TM level change handler function to the platform */
status = hddDevTmRegisterNotifyCallback(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmRegisterNotifyCallback failed",__func__);
goto err_unregister_pmops;
}
// register net device notifier for device change notification
ret = register_netdevice_notifier(&hdd_netdev_notifier);
if(ret < 0)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: register_netdevice_notifier failed",__func__);
goto err_unregister_pmops;
}
//Initialize the BTC service
if(btc_activate_service(pHddCtx) != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: btc_activate_service failed",__func__);
goto err_reg_netdev;
}
#ifdef FEATURE_OEM_DATA_SUPPORT
//Initialize the OEM service
if (oem_activate_service(pHddCtx) != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: oem_activate_service failed", __func__);
goto err_btc_activate_service;
}
#endif
#ifdef PTT_SOCK_SVC_ENABLE
//Initialize the PTT service
if(ptt_sock_activate_svc(pHddCtx) != 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: ptt_sock_activate_svc failed",__func__);
goto err_oem_activate_service;
}
#endif
#ifdef WLAN_FEATURE_RMC
if (hdd_open_cesium_nl_sock() < 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_open_cesium_nl_sock failed", __func__);
goto err_ptt_sock_activate_svc;
}
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
if(pHddCtx->cfg_ini && pHddCtx->cfg_ini->wlanLoggingEnable)
{
if(wlan_logging_sock_activate_svc(
pHddCtx->cfg_ini->wlanLoggingFEToConsole,
pHddCtx->cfg_ini->wlanLoggingNumBuf,
pHddCtx->cfg_ini->wlanPerPktStatsLogEnable,
pHddCtx->cfg_ini->wlanPerPktStatsNumBuf))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: wlan_logging_sock_activate_svc"
" failed", __func__);
goto err_open_cesium_nl_sock;
}
//TODO: To Remove enableDhcpDebug and use gEnableDebugLog for
//EAPOL and DHCP
if (!pHddCtx->cfg_ini->gEnableDebugLog)
pHddCtx->cfg_ini->gEnableDebugLog =
VOS_PKT_PROTO_TYPE_EAPOL | VOS_PKT_PROTO_TYPE_DHCP |
VOS_PKT_PROTO_TYPE_ARP;
}
if (pHddCtx->cfg_ini->wlanLoggingEnable &&
(pHddCtx->cfg_ini->enableFWLogging ||
pHddCtx->cfg_ini->enableMgmtLogging ||
pHddCtx->cfg_ini->enableContFWLogging))
{
hdd_init_frame_logging(pHddCtx);
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Logging disabled in ini"));
}
#endif
if (vos_is_multicast_logging())
wlan_logging_set_log_level();
hdd_register_mcast_bcast_filter(pHddCtx);
/* Action frame registered in one adapter which will
* applicable to all interfaces
*/
wlan_hdd_cfg80211_register_frames(pAdapter);
mutex_init(&pHddCtx->sap_lock);
mutex_init(&pHddCtx->roc_lock);
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
/* Initialize the wake lcok */
vos_wake_lock_init(&pHddCtx->rx_wake_lock,
"qcom_rx_wakelock");
#endif
/* Initialize the wake lcok */
vos_wake_lock_init(&pHddCtx->sap_wake_lock,
"qcom_sap_wakelock");
vos_event_init(&pHddCtx->scan_info.scan_finished_event);
pHddCtx->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP;
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_NO_LOAD_UNLOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
#ifdef FEATURE_WLAN_SCAN_PNO
/*SME must send channel update configuration to RIVA*/
sme_UpdateChannelConfig(pHddCtx->hHal);
#endif
/* Send the update default channel list to the FW*/
sme_UpdateChannelList(pHddCtx->hHal);
/* Fwr capabilities received, Set the Dot11 mode */
sme_SetPhyMode(WLAN_HDD_GET_HAL_CTX(pAdapter),
hdd_cfg_xlate_to_csr_phy_mode(pHddCtx->cfg_ini->dot11Mode));
sme_SetDefDot11Mode(pHddCtx->hHal);
#ifndef CONFIG_ENABLE_LINUX_REG
/*updating wiphy so that regulatory user hints can be processed*/
if (wiphy)
{
regulatory_hint(wiphy, "00");
}
#endif
// Initialize the restart logic
wlan_hdd_restart_init(pHddCtx);
if (pHddCtx->cfg_ini->fIsLogpEnabled) {
vos_wdthread_init_timer_work(vos_process_wd_timer);
/* Initialize the timer to detect thread stuck issues */
vos_thread_stuck_timer_init(
&((VosContextType*)pVosContext)->vosWatchdog);
}
//Register the traffic monitor timer now
if ( pHddCtx->cfg_ini->dynSplitscan)
{
vos_timer_init(&pHddCtx->tx_rx_trafficTmr,
VOS_TIMER_TYPE_SW,
hdd_tx_rx_pkt_cnt_stat_timer_handler,
(void *)pHddCtx);
}
wlan_hdd_cfg80211_nan_init(pHddCtx);
mutex_init(&pHddCtx->cur_rx_level_lock);
vos_timer_init(&pHddCtx->delack_timer, VOS_TIMER_TYPE_SW,
hdd_tcp_delack_compute_function,(void *)pHddCtx);
vos_timer_init(&pHddCtx->tdls_source_timer, VOS_TIMER_TYPE_SW,
wlan_hdd_change_tdls_mode, (void *)pHddCtx);
#ifdef WLAN_FEATURE_EXTSCAN
sme_EXTScanRegisterCallback(pHddCtx->hHal,
wlan_hdd_cfg80211_extscan_callback,
pHddCtx);
#endif /* WLAN_FEATURE_EXTSCAN */
#ifdef FEATURE_OEM_DATA_SUPPORT
sme_OemDataRegisterCallback(pHddCtx->hHal,
wlan_hdd_cfg80211_oemdata_callback,
pHddCtx);
#endif /* FEATURE_OEM_DATA_SUPPORT */
sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached_cb);
#ifdef WLAN_NS_OFFLOAD
// Register IPv6 notifier to notify if any change in IP
// So that we can reconfigure the offload parameters
pHddCtx->ipv6_notifier.notifier_call = wlan_hdd_ipv6_changed;
ret = register_inet6addr_notifier(&pHddCtx->ipv6_notifier);
if (ret)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to register IPv6 notifier"));
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Registered IPv6 notifier"));
}
#endif
vos_mem_set((uint8_t *)&pHddCtx->bad_sta, HDD_MAX_STA_COUNT, 0);
// Register IPv4 notifier to notify if any change in IP
// So that we can reconfigure the offload parameters
pHddCtx->ipv4_notifier.notifier_call = wlan_hdd_ipv4_changed;
ret = register_inetaddr_notifier(&pHddCtx->ipv4_notifier);
if (ret)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Failed to register IPv4 notifier"));
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO, FL("Registered IPv4 notifier"));
}
hdd_dp_util_send_rps_ind(pHddCtx);
pHddCtx->is_ap_mode_wow_supported =
sme_IsFeatureSupportedByFW(SAP_MODE_WOW);
pHddCtx->is_fatal_event_log_sup =
sme_IsFeatureSupportedByFW(FATAL_EVENT_LOGGING);
hddLog(VOS_TRACE_LEVEL_INFO, FL("FATAL_EVENT_LOGGING: %d"),
pHddCtx->is_fatal_event_log_sup);
hdd_assoc_registerFwdEapolCB(pVosContext);
mutex_init(&pHddCtx->cache_channel_lock);
goto success;
err_open_cesium_nl_sock:
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
hdd_close_cesium_nl_sock();
#endif
err_ptt_sock_activate_svc:
#ifdef PTT_SOCK_SVC_ENABLE
ptt_sock_deactivate_svc(pHddCtx);
#endif
err_oem_activate_service:
#ifdef FEATURE_OEM_DATA_SUPPORT
oem_deactivate_service();
#endif
err_btc_activate_service:
btc_deactivate_service();
err_reg_netdev:
unregister_netdevice_notifier(&hdd_netdev_notifier);
err_unregister_pmops:
hddDevTmUnregisterNotifyCallback(pHddCtx);
hddDeregisterPmOps(pHddCtx);
hdd_debugfs_exit(pHddCtx);
#ifdef WLAN_BTAMP_FEATURE
err_bap_stop:
WLANBAP_Stop(pVosContext);
#endif
#ifdef WLAN_BTAMP_FEATURE
err_bap_close:
WLANBAP_Close(pVosContext);
#endif
err_close_adapter:
hdd_close_all_adapters( pHddCtx );
#ifdef CONFIG_ENABLE_LINUX_REG
err_unregister_wiphy:
#endif
wiphy_unregister(wiphy) ;
hdd_wlan_free_wiphy_channels(wiphy);
err_vosstop:
vos_stop(pVosContext);
err_vosclose:
status = vos_sched_close( pVosContext );
if (!VOS_IS_STATUS_SUCCESS(status)) {
VOS_TRACE( VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: Failed to close VOSS Scheduler", __func__);
VOS_ASSERT( VOS_IS_STATUS_SUCCESS( status ) );
}
vos_close(pVosContext );
err_nl_srv:
#ifdef WLAN_KD_READY_NOTIFIER
nl_srv_exit(pHddCtx->ptt_pid);
#else
nl_srv_exit();
#endif /* WLAN_KD_READY_NOTIFIER */
err_vos_nv_close:
#ifdef CONFIG_ENABLE_LINUX_REG
vos_nv_close();
#endif
err_wdclose:
if(pHddCtx->cfg_ini->fIsLogpEnabled)
vos_watchdog_close(pVosContext);
err_config:
hdd_request_manager_deinit();
kfree(pHddCtx->cfg_ini);
pHddCtx->cfg_ini= NULL;
err_free_hdd_context:
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
free_riva_power_on_lock("wlan");
wiphy_free(wiphy) ;
//kfree(wdev) ;
VOS_BUG(1);
if (hdd_is_ssr_required())
{
/* WDI timeout had happened during load, so SSR is needed here */
subsystem_restart("wcnss");
msleep(5000);
}
hdd_set_ssr_required (VOS_FALSE);
return -EIO;
success:
EXIT();
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_driver_init() - Core Driver Init Function
This is the driver entry point - called in different timeline depending
on whether the driver is statically or dynamically linked
\param - None
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
static int hdd_driver_init( void)
{
VOS_STATUS status;
v_CONTEXT_t pVosContext = NULL;
struct device *dev = NULL;
int ret_status = 0;
#ifdef HAVE_WCNSS_CAL_DOWNLOAD
int max_retries = 0;
#endif
#ifdef HAVE_CBC_DONE
int max_cbc_retries = 0;
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_init_svc();
#endif
ENTER();
vos_wake_lock_init(&wlan_wake_lock, "wlan");
pr_info("%s: loading driver v%s\n", WLAN_MODULE_NAME,
QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR);
#ifdef ANI_BUS_TYPE_PCI
dev = wcnss_wlan_get_device();
#endif // ANI_BUS_TYPE_PCI
#ifdef ANI_BUS_TYPE_PLATFORM
#ifdef HAVE_WCNSS_CAL_DOWNLOAD
/* wait until WCNSS driver downloads NV */
while (!wcnss_device_ready() && 10 >= ++max_retries) {
msleep(1000);
}
if (max_retries >= 10) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: WCNSS driver not ready", __func__);
vos_wake_lock_destroy(&wlan_wake_lock);
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_deinit_svc();
#endif
return -ENODEV;
}
#endif
#ifdef HAVE_CBC_DONE
while (!wcnss_cbc_complete() && 10 >= ++max_cbc_retries) {
msleep(1000);
}
if (max_cbc_retries >= 10) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s:CBC not completed", __func__);
}
#endif
dev = wcnss_wlan_get_device();
#endif // ANI_BUS_TYPE_PLATFORM
do {
if (NULL == dev) {
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN device not found!!",__func__);
ret_status = -1;
break;
}
#ifdef TIMER_MANAGER
vos_timer_manager_init();
#endif
/* Preopen VOSS so that it is ready to start at least SAL */
status = vos_preOpen(&pVosContext);
if (!VOS_IS_STATUS_SUCCESS(status))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed to preOpen VOSS", __func__);
ret_status = -1;
break;
}
hddTraceInit();
hdd_register_debug_callback();
#ifndef MODULE
/* For statically linked driver, call hdd_set_conparam to update curr_con_mode
*/
hdd_set_conparam((v_UINT_t)con_mode);
#endif
// Call our main init function
if (hdd_wlan_startup(dev))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: WLAN Driver Initialization failed",
__func__);
vos_preClose( &pVosContext );
ret_status = -1;
break;
}
} while (0);
if (0 != ret_status)
{
#ifdef TIMER_MANAGER
vos_timer_exit();
#endif
#ifdef MEMORY_DEBUG
vos_mem_exit();
#endif
vos_wake_lock_destroy(&wlan_wake_lock);
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_deinit_svc();
#endif
pr_err("%s: driver load failure\n", WLAN_MODULE_NAME);
}
else
{
//Send WLAN UP indication to Nlink Service
send_btc_nlink_msg(WLAN_MODULE_UP_IND, 0);
pr_info("%s: driver loaded\n", WLAN_MODULE_NAME);
}
EXIT();
return ret_status;
}
/**---------------------------------------------------------------------------
\brief hdd_module_init() - Init Function
This is the driver entry point (invoked when module is loaded using insmod)
\param - None
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
#ifdef MODULE
static int __init hdd_module_init ( void)
{
return hdd_driver_init();
}
#else /* #ifdef MODULE */
static int __init hdd_module_init ( void)
{
/* Driver initialization is delayed to fwpath_changed_handler */
return 0;
}
#endif /* #ifdef MODULE */
/**---------------------------------------------------------------------------
\brief hdd_driver_exit() - Exit function
This is the driver exit point (invoked when module is unloaded using rmmod
or con_mode was changed by userspace)
\param - None
\return - None
--------------------------------------------------------------------------*/
static void hdd_driver_exit(void)
{
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext = NULL;
v_REGDOMAIN_t regId;
unsigned long rc = 0;
pr_info("%s: unloading driver v%s\n", WLAN_MODULE_NAME, QWLAN_VERSIONSTR);
//Get the global vos context
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if(!pVosContext)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
goto done;
}
//Get the HDD context.
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext );
if(!pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: module exit called before probe",__func__);
}
else if (VOS_MONITOR_MODE == hdd_get_conparam())
{
hddLog(VOS_TRACE_LEVEL_INFO,"%s: MONITOR MODE",__func__);
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_UNLOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
hdd_wlan_exit(pHddCtx);
vos_preClose( &pVosContext );
goto done;
}
else
{
/* We wait for active entry threads to exit from driver
* by waiting until rtnl_lock is available.
*/
rtnl_lock();
rtnl_unlock();
INIT_COMPLETION(pHddCtx->ssr_comp_var);
if ((pHddCtx->isLogpInProgress) && (FALSE ==
vos_is_wlan_in_badState(VOS_MODULE_ID_HDD, NULL)))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s:SSR in Progress; block rmmod !!!", __func__);
rc = wait_for_completion_timeout(&pHddCtx->ssr_comp_var,
msecs_to_jiffies(30000));
if(!rc)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:SSR timedout, fatal error", __func__);
VOS_BUG(0);
}
}
pHddCtx->isLoadUnloadInProgress = WLAN_HDD_UNLOAD_IN_PROGRESS;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
/* Driver Need to send country code 00 in below condition
* 1) If gCountryCodePriority is set to 1; and last country
* code set is through 11d. This needs to be done in case
* when NV country code is 00.
* This Needs to be done as when kernel store last country
* code and if stored country code is not through 11d,
* in sme_HandleChangeCountryCodeByUser we will disable 11d
* in next load/unload as soon as we get any country through
* 11d. In sme_HandleChangeCountryCodeByUser
* pMsg->countryCode will be last countryCode and
* pMac->scan.countryCode11d will be country through 11d so
* due to mismatch driver will disable 11d.
*
*/
if ((eANI_BOOLEAN_TRUE == sme_Is11dCountrycode(pHddCtx->hHal) &&
pHddCtx->cfg_ini->fSupplicantCountryCodeHasPriority &&
sme_Is11dSupported(pHddCtx->hHal)))
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("CountryCode 00 is being set while unloading driver"));
vos_nv_getRegDomainFromCountryCode(&regId , "00", COUNTRY_USER);
}
//Do all the cleanup before deregistering the driver
hdd_wlan_exit(pHddCtx);
}
vos_preClose( &pVosContext );
#ifdef TIMER_MANAGER
vos_timer_exit();
#endif
#ifdef MEMORY_DEBUG
vos_mem_exit();
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_deinit_svc();
#endif
done:
vos_wake_lock_destroy(&wlan_wake_lock);
pr_info("%s: driver unloaded\n", WLAN_MODULE_NAME);
}
/**---------------------------------------------------------------------------
\brief hdd_module_exit() - Exit function
This is the driver exit point (invoked when module is unloaded using rmmod)
\param - None
\return - None
--------------------------------------------------------------------------*/
static void __exit hdd_module_exit(void)
{
hdd_driver_exit();
}
#ifdef MODULE
static int fwpath_changed_handler(const char *kmessage,
const struct kernel_param *kp)
{
return param_set_copystring(kmessage, kp);
}
static int con_mode_handler(const char *kmessage,
const struct kernel_param *kp)
{
return param_set_int(kmessage, kp);
}
#else /* #ifdef MODULE */
/**---------------------------------------------------------------------------
\brief kickstart_driver
This is the driver entry point
- delayed driver initialization when driver is statically linked
- invoked when module parameter fwpath is modified from userspace to signal
initializing the WLAN driver or when con_mode is modified from userspace
to signal a switch in operating mode
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
static int kickstart_driver(void)
{
int ret_status;
if (!wlan_hdd_inited) {
ret_status = hdd_driver_init();
wlan_hdd_inited = ret_status ? 0 : 1;
return ret_status;
}
hdd_driver_exit();
msleep(200);
ret_status = hdd_driver_init();
wlan_hdd_inited = ret_status ? 0 : 1;
return ret_status;
}
/**---------------------------------------------------------------------------
\brief fwpath_changed_handler() - Handler Function
Handle changes to the fwpath parameter
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
static int fwpath_changed_handler(const char *kmessage,
const struct kernel_param *kp)
{
int ret;
ret = param_set_copystring(kmessage, kp);
if (0 == ret)
ret = kickstart_driver();
return ret;
}
/**---------------------------------------------------------------------------
\brief con_mode_handler() -
Handler function for module param con_mode when it is changed by userspace
Dynamically linked - do nothing
Statically linked - exit and init driver, as in rmmod and insmod
\param -
\return -
--------------------------------------------------------------------------*/
static int con_mode_handler(const char *kmessage,
const struct kernel_param *kp)
{
int ret;
ret = param_set_int(kmessage, kp);
if (0 == ret)
ret = kickstart_driver();
return ret;
}
#endif /* #ifdef MODULE */
/**---------------------------------------------------------------------------
\brief hdd_get_conparam() -
This is the driver exit point (invoked when module is unloaded using rmmod)
\param - None
\return - tVOS_CON_MODE
--------------------------------------------------------------------------*/
tVOS_CON_MODE hdd_get_conparam ( void )
{
#ifdef MODULE
return (tVOS_CON_MODE)con_mode;
#else
return (tVOS_CON_MODE)curr_con_mode;
#endif
}
void hdd_set_conparam ( v_UINT_t newParam )
{
con_mode = newParam;
#ifndef MODULE
curr_con_mode = con_mode;
#endif
}
/**---------------------------------------------------------------------------
\brief hdd_softap_sta_deauth() - function
This to take counter measure to handle deauth req from HDD
\param - pAdapter - Pointer to the HDD
\param - enable - boolean value
\return - None
--------------------------------------------------------------------------*/
VOS_STATUS hdd_softap_sta_deauth(hdd_adapter_t *pAdapter,
struct tagCsrDelStaParams *pDelStaParams)
{
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
VOS_STATUS vosStatus = VOS_STATUS_E_FAULT;
struct hdd_cache_sta_info *cache_sta_info;
ptSapContext pSapCtx = VOS_GET_SAP_CB(pVosContext);
ENTER();
hddLog(LOG1, "hdd_softap_sta_deauth:(%pK, false)",
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
if (!pSapCtx) {
hddLog(LOGE, "sap context is NULL");
return vosStatus;
}
cache_sta_info = hdd_get_cache_stainfo(pSapCtx->cache_sta_info,
pDelStaParams->peerMacAddr);
if (cache_sta_info) {
cache_sta_info->reason_code = pDelStaParams->reason_code;
cache_sta_info->rx_rate =
wlan_tl_get_sta_rx_rate(pVosContext, cache_sta_info->ucSTAId);
WLANTL_GetSAPStaRSSi(pVosContext, cache_sta_info->ucSTAId,
&cache_sta_info->rssi);
}
//Ignore request to deauth bcmc station
if (pDelStaParams->peerMacAddr[0] & 0x1)
return vosStatus;
vosStatus = WLANSAP_DeauthSta(pVosContext, pDelStaParams);
EXIT();
return vosStatus;
}
/**---------------------------------------------------------------------------
\brief hdd_del_all_sta() - function
This function removes all the stations associated on stopping AP/P2P GO.
\param - pAdapter - Pointer to the HDD
\return - None
--------------------------------------------------------------------------*/
int hdd_del_all_sta(hdd_adapter_t *pAdapter)
{
v_U16_t i;
VOS_STATUS vos_status;
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 1;
}
ENTER();
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Delete all STAs associated.",__func__);
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP)
|| (pAdapter->device_mode == WLAN_HDD_P2P_GO)
)
{
for(i = 0; i < WLAN_MAX_STA_COUNT; i++)
{
if ((pSapCtx->aStaInfo[i].isUsed) &&
(!pSapCtx->aStaInfo[i].isDeauthInProgress))
{
struct tagCsrDelStaParams delStaParams;
WLANSAP_PopulateDelStaParams(
pSapCtx->aStaInfo[i].macAddrSTA.bytes,
eSIR_MAC_DEAUTH_LEAVING_BSS_REASON,
SIR_MAC_MGMT_DEAUTH >> 4,
&delStaParams);
vos_status = hdd_softap_sta_deauth(pAdapter, &delStaParams);
if (VOS_IS_STATUS_SUCCESS(vos_status))
pSapCtx->aStaInfo[i].isDeauthInProgress = TRUE;
}
}
}
EXIT();
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_softap_sta_disassoc() - function
This to take counter measure to handle deauth req from HDD
\param - pAdapter - Pointer to the HDD
\param - enable - boolean value
\return - None
--------------------------------------------------------------------------*/
void hdd_softap_sta_disassoc(hdd_adapter_t *pAdapter,v_U8_t *pDestMacAddress)
{
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
struct hdd_cache_sta_info *cache_sta_info;
ptSapContext pSapCtx = VOS_GET_SAP_CB(pVosContext);
ENTER();
hddLog( LOGE, "hdd_softap_sta_disassoc:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
if (!pSapCtx) {
hddLog(LOGE, "sap context is NULL");
return ;
}
//Ignore request to disassoc bcmc station
if( pDestMacAddress[0] & 0x1 )
return;
cache_sta_info = hdd_get_cache_stainfo(pSapCtx->cache_sta_info,
pDestMacAddress);
if (cache_sta_info) {
cache_sta_info->reason_code = eSIR_MAC_DEAUTH_LEAVING_BSS_REASON;
cache_sta_info->rx_rate =
wlan_tl_get_sta_rx_rate(pVosContext, cache_sta_info->ucSTAId);
WLANTL_GetSAPStaRSSi(pVosContext, cache_sta_info->ucSTAId,
&cache_sta_info->rssi);
}
WLANSAP_DisassocSta(pVosContext,pDestMacAddress);
}
void hdd_softap_tkip_mic_fail_counter_measure(hdd_adapter_t *pAdapter,v_BOOL_t enable)
{
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
ENTER();
hddLog( LOGE, "hdd_softap_tkip_mic_fail_counter_measure:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
WLANSAP_SetCounterMeasure(pVosContext, (v_BOOL_t)enable);
}
/**---------------------------------------------------------------------------
*
* \brief hdd_get__concurrency_mode() -
*
*
* \param - None
*
* \return - CONCURRENCY MODE
*
* --------------------------------------------------------------------------*/
tVOS_CONCURRENCY_MODE hdd_get_concurrency_mode ( void )
{
v_CONTEXT_t pVosContext = vos_get_global_context( VOS_MODULE_ID_HDD, NULL );
hdd_context_t *pHddCtx;
if (NULL != pVosContext)
{
pHddCtx = vos_get_context( VOS_MODULE_ID_HDD, pVosContext);
if (NULL != pHddCtx)
{
return (tVOS_CONCURRENCY_MODE)pHddCtx->concurrency_mode;
}
}
/* we are in an invalid state :( */
hddLog(LOGE, "%s: Invalid context", __func__);
return VOS_STA;
}
v_BOOL_t
wlan_hdd_is_GO_power_collapse_allowed (hdd_context_t* pHddCtx)
{
hdd_adapter_t *pAdapter = NULL;
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_GO);
if (pAdapter == NULL)
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("GO doesn't exist"));
return TRUE;
}
if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))
{
hddLog(VOS_TRACE_LEVEL_INFO,
FL("GO started"));
return TRUE;
}
else
/* wait till GO changes its interface to p2p device */
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Del_bss called, avoid apps suspend"));
return FALSE;
}
/* Decide whether to allow/not the apps power collapse.
* Allow apps power collapse if we are in connected state.
* if not, allow only if we are in IMPS */
v_BOOL_t hdd_is_apps_power_collapse_allowed(hdd_context_t* pHddCtx)
{
tPmcState pmcState = pmcGetPmcState(pHddCtx->hHal);
tANI_BOOLEAN scanRspPending = csrNeighborRoamScanRspPending(pHddCtx->hHal);
tANI_BOOLEAN inMiddleOfRoaming = csrNeighborMiddleOfRoaming(pHddCtx->hHal);
hdd_config_t *pConfig = pHddCtx->cfg_ini;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
VOS_STATUS status;
tVOS_CONCURRENCY_MODE concurrent_state = 0;
if (VOS_STA_SAP_MODE == hdd_get_conparam())
return TRUE;
concurrent_state = hdd_get_concurrency_mode();
if ((concurrent_state == (VOS_STA | VOS_P2P_GO)) &&
!(wlan_hdd_is_GO_power_collapse_allowed(pHddCtx)))
return FALSE;
#ifdef WLAN_ACTIVEMODE_OFFLOAD_FEATURE
if(((concurrent_state == (VOS_STA | VOS_P2P_CLIENT)) ||
(concurrent_state == (VOS_STA | VOS_P2P_GO)))&&
(IS_ACTIVEMODE_OFFLOAD_FEATURE_ENABLE))
return TRUE;
#endif
/*loop through all adapters. TBD fix for Concurrency */
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if ( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode)
|| (WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) )
{
if (((pConfig->fIsImpsEnabled || pConfig->fIsBmpsEnabled)
&& (pmcState != IMPS && pmcState != BMPS && pmcState != UAPSD
&& pmcState != STOPPED && pmcState != STANDBY &&
pmcState != WOWL)) ||
(eANI_BOOLEAN_TRUE == scanRspPending) ||
(eANI_BOOLEAN_TRUE == inMiddleOfRoaming))
{
if(pmcState == FULL_POWER &&
sme_IsCoexScoIndicationSet(pHddCtx->hHal))
{
/*
* When SCO indication comes from Coex module , host will
* enter in to full power mode, but this should not prevent
* apps processor power collapse.
*/
hddLog(LOG1,
FL("Allow apps power collapse"
"even when sco indication is set"));
return TRUE;
}
hddLog( LOGE, "%s: do not allow APPS power collapse-"
"pmcState = %d scanRspPending = %d "
"inMiddleOfRoaming = %d connected = %d",
__func__, pmcState, scanRspPending,
inMiddleOfRoaming, hdd_connIsConnected(
WLAN_HDD_GET_STATION_CTX_PTR( pAdapter )));
wlan_hdd_get_tdls_stats(pAdapter);
return FALSE;
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return TRUE;
}
/* Decides whether to send suspend notification to Riva
* if any adapter is in BMPS; then it is required */
v_BOOL_t hdd_is_suspend_notify_allowed(hdd_context_t* pHddCtx)
{
tPmcState pmcState = pmcGetPmcState(pHddCtx->hHal);
hdd_config_t *pConfig = pHddCtx->cfg_ini;
if (pConfig->fIsBmpsEnabled && (pmcState == BMPS))
{
return TRUE;
}
return FALSE;
}
void wlan_hdd_set_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
{
switch(mode)
{
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
case VOS_P2P_GO_MODE:
case VOS_STA_SAP_MODE:
case VOS_MONITOR_MODE:
pHddCtx->concurrency_mode |= (1 << mode);
pHddCtx->no_of_open_sessions[mode]++;
break;
default:
break;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("concurrency_mode = 0x%x "
"Number of open sessions for mode %d = %d"),
pHddCtx->concurrency_mode, mode,
pHddCtx->no_of_open_sessions[mode]);
}
void wlan_hdd_clear_concurrency_mode(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
{
switch(mode)
{
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
case VOS_P2P_GO_MODE:
case VOS_STA_SAP_MODE:
case VOS_MONITOR_MODE:
pHddCtx->no_of_open_sessions[mode]--;
if (!(pHddCtx->no_of_open_sessions[mode]))
pHddCtx->concurrency_mode &= (~(1 << mode));
break;
default:
break;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("concurrency_mode = 0x%x "
"Number of open sessions for mode %d = %d"),
pHddCtx->concurrency_mode, mode, pHddCtx->no_of_open_sessions[mode]);
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_incr_active_session()
*
* This function increments the number of active sessions
* maintained per device mode
* Incase of STA/P2P CLI/IBSS upon connection indication it is incremented
* Incase of SAP/P2P GO upon bss start it is incremented
*
* \param pHddCtx - HDD Context
* \param mode - device mode
*
* \return - None
*
* --------------------------------------------------------------------------*/
void wlan_hdd_incr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
{
switch (mode) {
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
case VOS_P2P_GO_MODE:
case VOS_STA_SAP_MODE:
pHddCtx->no_of_active_sessions[mode]++;
break;
default:
hddLog(VOS_TRACE_LEVEL_INFO, FL("Not Expected Mode %d"), mode);
break;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("No.# of active sessions for mode %d = %d"),
mode,
pHddCtx->no_of_active_sessions[mode]);
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_decr_active_session()
*
* This function decrements the number of active sessions
* maintained per device mode
* Incase of STA/P2P CLI/IBSS upon disconnection it is decremented
* Incase of SAP/P2P GO upon bss stop it is decremented
*
* \param pHddCtx - HDD Context
* \param mode - device mode
*
* \return - None
*
* --------------------------------------------------------------------------*/
void wlan_hdd_decr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
{
switch (mode) {
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
case VOS_P2P_GO_MODE:
case VOS_STA_SAP_MODE:
if (pHddCtx->no_of_active_sessions[mode] > 0)
pHddCtx->no_of_active_sessions[mode]--;
else
hddLog(VOS_TRACE_LEVEL_INFO, FL(" No.# of Active sessions"
"is already Zero"));
break;
default:
hddLog(VOS_TRACE_LEVEL_INFO, FL("Not Expected Mode %d"), mode);
break;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("No.# of active sessions for mode %d = %d"),
mode,
pHddCtx->no_of_active_sessions[mode]);
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_restart_init
*
* This function initalizes restart timer/flag. An internal function.
*
* \param - pHddCtx
*
* \return - None
*
* --------------------------------------------------------------------------*/
static void wlan_hdd_restart_init(hdd_context_t *pHddCtx)
{
/* Initialize */
pHddCtx->hdd_restart_retries = 0;
atomic_set(&pHddCtx->isRestartInProgress, 0);
vos_timer_init(&pHddCtx->hdd_restart_timer,
VOS_TIMER_TYPE_SW,
wlan_hdd_restart_timer_cb,
pHddCtx);
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_restart_deinit
*
* This function cleans up the resources used. An internal function.
*
* \param - pHddCtx
*
* \return - None
*
* --------------------------------------------------------------------------*/
static void wlan_hdd_restart_deinit(hdd_context_t* pHddCtx)
{
VOS_STATUS vos_status;
/* Block any further calls */
atomic_set(&pHddCtx->isRestartInProgress, 1);
/* Cleanup */
vos_status = vos_timer_stop( &pHddCtx->hdd_restart_timer );
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to stop HDD restart timer"));
vos_status = vos_timer_destroy(&pHddCtx->hdd_restart_timer);
if (!VOS_IS_STATUS_SUCCESS(vos_status))
hddLog(LOGE, FL("Failed to destroy HDD restart timer"));
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_framework_restart
*
* This function uses a cfg80211 API to start a framework initiated WLAN
* driver module unload/load.
*
* Also this API keep retrying (WLAN_HDD_RESTART_RETRY_MAX_CNT).
*
*
* \param - pHddCtx
*
* \return - VOS_STATUS_SUCCESS: Success
* VOS_STATUS_E_EMPTY: Adapter is Empty
* VOS_STATUS_E_NOMEM: No memory
* --------------------------------------------------------------------------*/
static VOS_STATUS wlan_hdd_framework_restart(hdd_context_t *pHddCtx)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
int len = (sizeof (struct ieee80211_mgmt));
struct ieee80211_mgmt *mgmt = NULL;
/* Prepare the DEAUTH managment frame with reason code */
mgmt = kzalloc(len, GFP_KERNEL);
if(mgmt == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s: memory allocation failed (%d bytes)", __func__, len);
return VOS_STATUS_E_NOMEM;
}
mgmt->u.deauth.reason_code = WLAN_REASON_DISASSOC_LOW_ACK;
/* Iterate over all adapters/devices */
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
if ((NULL == pAdapterNode) || (VOS_STATUS_SUCCESS != status))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("fail to get adapter: %pK %d"), pAdapterNode, status);
goto end;
}
do
{
if(pAdapterNode->pAdapter &&
WLAN_HDD_ADAPTER_MAGIC == pAdapterNode->pAdapter->magic)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"restarting the driver(intf:\'%s\' mode:%d :try %d)",
pAdapterNode->pAdapter->dev->name,
pAdapterNode->pAdapter->device_mode,
pHddCtx->hdd_restart_retries + 1);
/*
* CFG80211 event to restart the driver
*
* 'cfg80211_send_unprot_deauth' sends a
* NL80211_CMD_UNPROT_DEAUTHENTICATE event to supplicant at any state
* of SME(Linux Kernel) state machine.
*
* Reason code WLAN_REASON_DISASSOC_LOW_ACK is currently used to restart
* the driver.
*
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
cfg80211_rx_unprot_mlme_mgmt(pAdapterNode->pAdapter->dev, (u_int8_t*)mgmt, len);
#else
cfg80211_send_unprot_deauth(pAdapterNode->pAdapter->dev, (u_int8_t*)mgmt, len );
#endif
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
} while((NULL != pAdapterNode) && (VOS_STATUS_SUCCESS == status));
end:
/* Free the allocated management frame */
kfree(mgmt);
/* Retry until we unload or reach max count */
if(++pHddCtx->hdd_restart_retries < WLAN_HDD_RESTART_RETRY_MAX_CNT)
vos_timer_start(&pHddCtx->hdd_restart_timer, WLAN_HDD_RESTART_RETRY_DELAY_MS);
return status;
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_restart_timer_cb
*
* Restart timer callback. An internal function.
*
* \param - User data:
*
* \return - None
*
* --------------------------------------------------------------------------*/
void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback)
{
hdd_context_t *pHddCtx = usrDataForCallback;
wlan_hdd_framework_restart(pHddCtx);
return;
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_restart_driver
*
* This function sends an event to supplicant to restart the WLAN driver.
*
* This function is called from vos_wlanRestart.
*
* \param - pHddCtx
*
* \return - VOS_STATUS_SUCCESS: Success
* VOS_STATUS_E_EMPTY: Adapter is Empty
* VOS_STATUS_E_ALREADY: Request already in progress
* --------------------------------------------------------------------------*/
VOS_STATUS wlan_hdd_restart_driver(hdd_context_t *pHddCtx)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
/* A tight check to make sure reentrancy */
if(atomic_xchg(&pHddCtx->isRestartInProgress, 1))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLAN restart is already in progress", __func__);
return VOS_STATUS_E_ALREADY;
}
/* Send reset FIQ to WCNSS to invoke SSR. */
#ifdef HAVE_WCNSS_RESET_INTR
wcnss_reset_fiq(TRUE);
#endif
return status;
}
/**
* hdd_get_total_sessions() - provide total number of active sessions
* @pHddCtx: Valid Global HDD context pointer
*
* This function iterates through pAdaptors and find the number of all active
* sessions. This active sessions includes connected sta, p2p client and number
* of client connected to sap/p2p go.
*
* Return: Total number of active sessions.
*/
v_U8_t hdd_get_total_sessions(hdd_context_t *pHddCtx)
{
v_U8_t active_session = 0;
hdd_station_ctx_t *pHddStaCtx;
hdd_adapter_list_node_t *pAdapterNode, *pNext;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pAdapter = pAdapterNode->pAdapter;
switch (pAdapter->device_mode) {
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if(eConnectionState_Associated == pHddStaCtx->conn_info.connState)
active_session += 1;
break;
case VOS_STA_SAP_MODE:
case VOS_P2P_GO_MODE:
active_session += hdd_softap_get_connected_sta(pAdapter);
break;
default:
break;
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
return active_session;
}
/**
* hdd_set_delack_value() - Set delack value
* @pHddCtx: Valid Global HDD context pointer
* @next_rx_level: Value to set for delack
*
* This function compare present value and next value of delack. If the both
* are diffrent then it sets next value .
*
* Return: void.
*/
void hdd_set_delack_value(hdd_context_t *pHddCtx, v_U32_t next_rx_level)
{
if (pHddCtx->cur_rx_level != next_rx_level) {
struct wlan_rx_tp_data rx_tp_data = {0};
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: TCP DELACK trigger level %d",
__func__, next_rx_level);
mutex_lock(&pHddCtx->cur_rx_level_lock);
pHddCtx->cur_rx_level = next_rx_level;
mutex_unlock(&pHddCtx->cur_rx_level_lock);
rx_tp_data.rx_tp_flags |= TCP_DEL_ACK_IND;
rx_tp_data.level = next_rx_level;
wlan_hdd_send_svc_nlink_msg(WLAN_SVC_WLAN_TP_IND, &rx_tp_data,
sizeof(rx_tp_data));
}
}
/**
* hdd_set_default_stop_delack_timer() - Start delack timer
* @pHddCtx: Valid Global HDD context pointer
*
* This function stop delack timer and set delack value to default..
*
* Return: void.
*/
void hdd_set_default_stop_delack_timer(hdd_context_t *pHddCtx)
{
if (VOS_TIMER_STATE_RUNNING !=
vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: Can not stop timer", __func__);
return;
}
vos_timer_stop(&pHddCtx->delack_timer);
hdd_set_delack_value(pHddCtx, WLAN_SVC_TP_LOW);
}
/**
* hdd_start_delack_timer() - Start delack timer
* @pHddCtx: Valid Global HDD context pointer
*
* This function starts the delack timer for tcpDelAckComputeInterval time
* interval.The default timer value is 2 second.
*
* Return: void.
*/
void hdd_start_delack_timer(hdd_context_t *pHddCtx)
{
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: Timer is already running", __func__);
return;
}
vos_timer_start(&pHddCtx->delack_timer,
pHddCtx->cfg_ini->tcpDelAckComputeInterval);
}
/**
* hdd_update_prev_rx_packet_count() - Update previous rx packet count
* @pHddCtx: Valid Global HDD context pointer
*
* This function updates the prev_rx_packets count from the corresponding
* pAdapter states. This prev_rx_packets will diffed with the packet count
* at the end of delack timer. That can give number of RX packet is spacific
* time.
*
* Return: void.
*/
void hdd_update_prev_rx_packet_count(hdd_context_t *pHddCtx)
{
hdd_adapter_list_node_t *pAdapterNode, *pNext;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pAdapter = pAdapterNode->pAdapter;
pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
/**
* hdd_manage_delack_timer() - start\stop delack timer
* @pHddCtx: Valid Global HDD context pointer
*
* This function check the number of concerent session present, it starts the
* delack timer if only one session is present.
* In the case of BT_COEX and TDLS mode it blindly stop delack functionality.
*
* Return: void.
*/
void hdd_manage_delack_timer(hdd_context_t *pHddCtx)
{
uint8_t sessions;
if (!pHddCtx->cfg_ini->enable_delack) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: TCP DELACK is not enabled", __func__);
return;
}
/* Blindly stop timer of BTCOEX and TDLS Session is up */
if (pHddCtx->mode != 0) {
hdd_set_default_stop_delack_timer(pHddCtx);
return;
}
sessions = hdd_get_total_sessions(pHddCtx);
if (sessions == 1) {
hdd_update_prev_rx_packet_count(pHddCtx);
hdd_start_delack_timer(pHddCtx);
} else {
hdd_set_default_stop_delack_timer(pHddCtx);
}
}
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_init_channels
*
* This function is used to initialize the channel list in CSR
*
* This function is called from hdd_wlan_startup
*
* \param - pHddCtx: HDD context
*
* \return - VOS_STATUS_SUCCESS: Success
* VOS_STATUS_E_FAULT: Failure reported by SME
* --------------------------------------------------------------------------*/
static VOS_STATUS wlan_hdd_init_channels(hdd_context_t *pHddCtx)
{
eHalStatus status;
status = sme_InitChannels(pHddCtx->hHal);
if (HAL_STATUS_SUCCESS(status))
{
return VOS_STATUS_SUCCESS;
}
else
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Channel initialization failed(%d)",
__func__, status);
return VOS_STATUS_E_FAULT;
}
}
#ifdef CONFIG_ENABLE_LINUX_REG
VOS_STATUS wlan_hdd_init_channels_for_cc(hdd_context_t *pHddCtx, driver_load_type init )
{
eHalStatus status;
if (init == INIT && init_by_reg_core_user) {
init_by_reg_core_user = false;
pr_info("driver regulatory hint is not required");
return VOS_STATUS_SUCCESS;
}
status = sme_InitChannelsForCC(pHddCtx->hHal, init);
if (HAL_STATUS_SUCCESS(status))
{
return VOS_STATUS_SUCCESS;
}
else
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Issue reg hint failed(%d)",
__func__, status);
return VOS_STATUS_E_FAULT;
}
}
#endif
/*
* API to find if there is any STA or P2P-Client is connected
*/
VOS_STATUS hdd_issta_p2p_clientconnected(hdd_context_t *pHddCtx)
{
return sme_isSta_p2p_clientConnected(pHddCtx->hHal);
}
/*
* API to find if the firmware will send logs using DXE channel
*/
v_U8_t hdd_is_fw_logging_enabled(void)
{
hdd_context_t *pHddCtx;
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD,
vos_get_global_context(VOS_MODULE_ID_HDD, NULL));
return (pHddCtx && pHddCtx->cfg_ini->wlanLoggingEnable &&
pHddCtx->cfg_ini->enableMgmtLogging);
}
/*
* API to find if the firmware will send trace logs using DXE channel
*/
v_U8_t hdd_is_fw_ev_logging_enabled(void)
{
hdd_context_t *pHddCtx;
pHddCtx = vos_get_context(VOS_MODULE_ID_HDD,
vos_get_global_context(VOS_MODULE_ID_HDD, NULL));
return (pHddCtx && pHddCtx->cfg_ini->wlanLoggingEnable &&
pHddCtx->cfg_ini->enableFWLogging);
}
/*
* API to find if there is any session connected
*/
VOS_STATUS hdd_is_any_session_connected(hdd_context_t *pHddCtx)
{
return sme_is_any_session_connected(pHddCtx->hHal);
}
int wlan_hdd_scan_abort(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_scaninfo_t *pScanInfo = NULL;
long status = 0;
tSirAbortScanStatus abortScanStatus;
pScanInfo = &pHddCtx->scan_info;
INIT_COMPLETION(pScanInfo->abortscan_event_var);
if (pScanInfo->mScanPending)
{
abortScanStatus = hdd_abort_mac_scan(pHddCtx, pScanInfo->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("abortScanStatus: %d"), abortScanStatus);
/* If there is active scan command lets wait for the completion else
* there is no need to wait as scan command might be in the SME pending
* command list.
*/
if (abortScanStatus == eSIR_ABORT_ACTIVE_SCAN_LIST_NOT_EMPTY)
{
status = wait_for_completion_timeout(
&pScanInfo->abortscan_event_var,
msecs_to_jiffies(5000));
if (0 >= status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Timeout or Interrupt occurred while waiting for abort"
"scan, status- %ld", __func__, status);
return -ETIMEDOUT;
}
}
else if (abortScanStatus == eSIR_ABORT_SCAN_FAILURE)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("hdd_abort_mac_scan failed"));
return -VOS_STATUS_E_FAILURE;
}
}
return 0;
}
/**
* hdd_indicate_mgmt_frame() - Wrapper to indicate management frame to
* user space
* @frame_ind: Management frame data to be informed.
*
* This function is used to indicate management frame to
* user space
*
* Return: None
*
*/
void hdd_indicate_mgmt_frame(tSirSmeMgmtFrameInd *frame_ind)
{
hdd_context_t *hdd_ctx = NULL;
hdd_adapter_t *adapter = NULL;
v_CONTEXT_t vos_context = NULL;
struct ieee80211_mgmt *mgmt =
(struct ieee80211_mgmt *)frame_ind->frameBuf;
/* Get the global VOSS context.*/
vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if (!vos_context) {
hddLog(LOGE, FL("Global VOS context is Null"));
return;
}
/* Get the HDD context.*/
hdd_ctx =
(hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, vos_context );
if (0 != wlan_hdd_validate_context(hdd_ctx))
{
return;
}
if (frame_ind->frameLen < ieee80211_hdrlen(mgmt->frame_control)) {
hddLog(LOGE, FL(" Invalid frame length"));
return;
}
adapter = hdd_get_adapter_by_sme_session_id(hdd_ctx,
frame_ind->sessionId);
if ((NULL != adapter) &&
(WLAN_HDD_ADAPTER_MAGIC == adapter->magic))
__hdd_indicate_mgmt_frame(adapter,
frame_ind->frameLen,
frame_ind->frameBuf,
frame_ind->frameType,
frame_ind->rxChan,
frame_ind->rxRssi);
return;
}
VOS_STATUS wlan_hdd_cancel_remain_on_channel(hdd_context_t *pHddCtx)
{
hdd_adapter_t *pAdapter;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS vosStatus;
vosStatus = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while (NULL != pAdapterNode && VOS_STATUS_E_EMPTY != vosStatus)
{
pAdapter = pAdapterNode->pAdapter;
if (NULL != pAdapter)
{
if (WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ||
WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ||
WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
hddLog(LOG1, FL("abort ROC deviceMode: %d"),
pAdapter->device_mode);
if (VOS_STATUS_SUCCESS !=
wlan_hdd_cancel_existing_remain_on_channel(pAdapter))
{
hddLog(LOGE, FL("failed to abort ROC"));
return VOS_STATUS_E_FAILURE;
}
}
}
vosStatus = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return VOS_STATUS_SUCCESS;
}
hdd_remain_on_chan_ctx_t *hdd_get_remain_on_channel_ctx(hdd_context_t *pHddCtx)
{
hdd_adapter_t *pAdapter;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_cfg80211_state_t *cfgState;
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
VOS_STATUS vosStatus;
vosStatus = hdd_get_front_adapter (pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_E_EMPTY != vosStatus)
{
pAdapter = pAdapterNode->pAdapter;
if (NULL != pAdapter)
{
cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx)
break;
}
vosStatus = hdd_get_next_adapter (pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
return pRemainChanCtx;
}
/**
* wlan_hdd_handle_dfs_chan_scan () - handles disable/enable DFS channels
*
* @pHddCtx: HDD context within host driver
* @dfsScanMode: dfsScanMode passed from ioctl
*
*/
VOS_STATUS wlan_hdd_handle_dfs_chan_scan(hdd_context_t *pHddCtx,
tANI_U8 dfsScanMode)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS vosStatus;
hdd_station_ctx_t *pHddStaCtx;
eHalStatus status = eHAL_STATUS_SUCCESS;
if(!pHddCtx)
{
hddLog(LOGE, FL("HDD context is Null"));
return eHAL_STATUS_FAILURE;
}
if (pHddCtx->scan_info.mScanPending)
{
hddLog(LOG1, FL("Aborting scan for sessionId: %d"),
pHddCtx->scan_info.sessionId);
hdd_abort_mac_scan(pHddCtx,
pHddCtx->scan_info.sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
if (!dfsScanMode)
{
vosStatus = hdd_get_front_adapter( pHddCtx, &pAdapterNode);
while ((NULL != pAdapterNode) &&
(VOS_STATUS_SUCCESS == vosStatus))
{
pAdapter = pAdapterNode->pAdapter;
if (WLAN_HDD_INFRA_STATION == pAdapter->device_mode)
{
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if(!pHddStaCtx)
{
hddLog(LOGE, FL("HDD STA context is Null"));
return eHAL_STATUS_FAILURE;
}
/* if STA is already connected on DFS channel,
disconnect immediately*/
if (hdd_connIsConnected(pHddStaCtx) &&
(NV_CHANNEL_DFS ==
vos_nv_getChannelEnabledState(
pHddStaCtx->conn_info.operationChannel)))
{
status = sme_RoamDisconnect(pHddCtx->hHal,
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
hddLog(LOG1, FL("Client connected on DFS channel %d,"
"sme_RoamDisconnect returned with status: %d"
"for sessionid: %d"), pHddStaCtx->conn_info.
operationChannel, status, pAdapter->sessionId);
}
}
vosStatus = hdd_get_next_adapter(pHddCtx, pAdapterNode,
&pNext);
pAdapterNode = pNext;
}
}
sme_UpdateDFSScanMode(pHddCtx->hHal, dfsScanMode);
sme_UpdateDFSRoamMode(pHddCtx->hHal,
(dfsScanMode != DFS_CHNL_SCAN_DISABLED));
status = sme_HandleDFSChanScan(pHddCtx->hHal);
if (!HAL_STATUS_SUCCESS(status))
{
hddLog(LOGE,
FL("Failed in sme_HandleDFSChanScan (err=%d)"), status);
return status;
}
return status;
}
static int hdd_log2_ceil(unsigned value)
{
/* need to switch to unsigned math so that negative values
* will right-shift towards 0 instead of -1
*/
unsigned tmp = value;
int log2 = -1;
if (value == 0)
return 0;
while (tmp) {
log2++;
tmp >>= 1;
}
if (1U << log2 != value)
log2++;
return log2;
}
/**
* hdd_sta_id_hash_attach() - initialize sta id to macaddr hash
* @pAdapter: adapter handle
*
* Return: vos status
*/
VOS_STATUS hdd_sta_id_hash_attach(hdd_adapter_t *pAdapter)
{
int hash_elem, log2, i;
spin_lock_bh( &pAdapter->sta_hash_lock);
if (pAdapter->is_sta_id_hash_initialized == VOS_TRUE) {
spin_unlock_bh( &pAdapter->sta_hash_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: hash already attached for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_SUCCESS;
}
spin_unlock_bh( &pAdapter->sta_hash_lock);
hash_elem = WLAN_MAX_STA_COUNT;
hash_elem *= HDD_STA_ID_HASH_MULTIPLIER;
log2 = hdd_log2_ceil(hash_elem);
hash_elem = 1 << log2;
pAdapter->sta_id_hash.mask = hash_elem - 1;
pAdapter->sta_id_hash.idx_bits = log2;
pAdapter->sta_id_hash.bins =
vos_mem_malloc(hash_elem *sizeof(hdd_list_t));
if (!pAdapter->sta_id_hash.bins) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: malloc failed for session %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_E_NOMEM;
}
for (i = 0; i < hash_elem; i++)
hdd_list_init(&pAdapter->sta_id_hash.bins[i], WLAN_MAX_STA_COUNT);
spin_lock_bh( &pAdapter->sta_hash_lock);
pAdapter->is_sta_id_hash_initialized = VOS_TRUE;
spin_unlock_bh( &pAdapter->sta_hash_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Station ID Hash attached for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_SUCCESS;
}
/**
* hdd_sta_id_hash_detach() - deinit sta_id to macaddr hash
* @pAdapter: adapter handle
*
* Return: vos status
*/
VOS_STATUS hdd_sta_id_hash_detach(hdd_adapter_t *pAdapter)
{
int hash_elem, i;
v_SIZE_t size;
spin_lock_bh( &pAdapter->sta_hash_lock);
if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
spin_unlock_bh( &pAdapter->sta_hash_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: hash not initialized for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_SUCCESS;
}
pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
spin_unlock_bh( &pAdapter->sta_hash_lock);
hash_elem = 1 << pAdapter->sta_id_hash.idx_bits;
/* free all station info*/
for (i = 0; i < hash_elem; i++) {
hdd_list_size(&pAdapter->sta_id_hash.bins[i], &size);
if (size != 0) {
VOS_STATUS status;
hdd_staid_hash_node_t *sta_info_node = NULL;
hdd_staid_hash_node_t *next_node = NULL;
status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[i],
(hdd_list_node_t**) &sta_info_node );
while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
{
status = hdd_list_remove_node( &pAdapter->sta_id_hash.bins[i],
&sta_info_node->node);
vos_mem_free(sta_info_node);
status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[i],
(hdd_list_node_t*)sta_info_node,
(hdd_list_node_t**)&next_node);
sta_info_node = next_node;
}
}
}
vos_mem_free(pAdapter->sta_id_hash.bins);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Station ID Hash detached for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_SUCCESS;
}
/**
* hdd_sta_id_hash_calculate_index() - derive index from macaddr
* @pAdapter: adapter handle
* @mac_addr_in: input mac address
*
* Return: index derived from mac address
*/
int hdd_sta_id_hash_calculate_index(hdd_adapter_t *pAdapter,
v_MACADDR_t *mac_addr_in)
{
uint16 index;
struct hdd_align_mac_addr_t * mac_addr =
(struct hdd_align_mac_addr_t *)mac_addr_in;
index = mac_addr->bytes_ab ^
mac_addr->bytes_cd ^ mac_addr->bytes_ef;
index ^= index >> pAdapter->sta_id_hash.idx_bits;
index &= pAdapter->sta_id_hash.mask;
return index;
}
/**
* hdd_sta_id_hash_add_entry() - add entry in hash
* @pAdapter: adapter handle
* @sta_id: station id
* @mac_addr: mac address
*
* Return: vos status
*/
VOS_STATUS hdd_sta_id_hash_add_entry(hdd_adapter_t *pAdapter,
v_U8_t sta_id, v_MACADDR_t *mac_addr)
{
uint16 index;
hdd_staid_hash_node_t *sta_info_node = NULL;
index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr);
sta_info_node = vos_mem_malloc(sizeof(hdd_staid_hash_node_t));
if (!sta_info_node) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: malloc failed", __func__);
return VOS_STATUS_E_NOMEM;
}
sta_info_node->sta_id = sta_id;
vos_mem_copy(&sta_info_node->mac_addr, mac_addr, sizeof(v_MACADDR_t));
spin_lock_bh( &pAdapter->sta_hash_lock);
if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
spin_unlock_bh( &pAdapter->sta_hash_lock);
vos_mem_free(sta_info_node);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: hash is not initialized for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_E_FAILURE;
}
hdd_list_insert_back ( &pAdapter->sta_id_hash.bins[index],
(hdd_list_node_t*) sta_info_node );
spin_unlock_bh( &pAdapter->sta_hash_lock);
return VOS_STATUS_SUCCESS;
}
/**
* hdd_sta_id_hash_remove_entry() - remove entry from hash
* @pAdapter: adapter handle
* @sta_id: station id
* @mac_addr: mac address
*
* Return: vos status
*/
VOS_STATUS hdd_sta_id_hash_remove_entry(hdd_adapter_t *pAdapter,
v_U8_t sta_id, v_MACADDR_t *mac_addr)
{
uint16 index;
VOS_STATUS status;
hdd_staid_hash_node_t *sta_info_node = NULL;
hdd_staid_hash_node_t *next_node = NULL;
spin_lock_bh( &pAdapter->sta_hash_lock);
if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
spin_unlock_bh( &pAdapter->sta_hash_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: hash is not initialized for session id %d",
__func__, pAdapter->sessionId);
return VOS_STATUS_E_FAILURE;
}
index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr);
status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[index],
(hdd_list_node_t**) &sta_info_node );
while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
{
if (sta_info_node->sta_id == sta_id) {
status = hdd_list_remove_node( &pAdapter->sta_id_hash.bins[index],
&sta_info_node->node);
vos_mem_free(sta_info_node);
break;
}
status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[index],
(hdd_list_node_t*)sta_info_node, (hdd_list_node_t**)&next_node);
sta_info_node = next_node;
}
spin_unlock_bh( &pAdapter->sta_hash_lock);
return status;
}
/**
* hdd_sta_id_find_from_mac_addr() - find sta id from mac address
* @pAdapter: adapter handle
* @mac_addr_in: mac address
*
* Return: station id
*/
int hdd_sta_id_find_from_mac_addr(hdd_adapter_t *pAdapter,
v_MACADDR_t *mac_addr_in)
{
uint8 is_found = 0;
uint8 sta_id = HDD_WLAN_INVALID_STA_ID;
uint16 index;
VOS_STATUS status;
hdd_staid_hash_node_t *sta_info_node = NULL;
hdd_staid_hash_node_t *next_node = NULL;
spin_lock_bh( &pAdapter->sta_hash_lock);
if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
spin_unlock_bh( &pAdapter->sta_hash_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("hash is not initialized for session id %d"),
pAdapter->sessionId);
return HDD_WLAN_INVALID_STA_ID;
}
index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr_in);
status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[index],
(hdd_list_node_t**) &sta_info_node );
while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
{
if (vos_mem_compare(&sta_info_node->mac_addr,
mac_addr_in, sizeof(v_MACADDR_t))) {
is_found = 1;
sta_id = sta_info_node->sta_id;
break;
}
status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[index],
(hdd_list_node_t*)sta_info_node,
(hdd_list_node_t**)&next_node);
sta_info_node = next_node;
}
spin_unlock_bh( &pAdapter->sta_hash_lock);
return sta_id;
}
void hdd_initialize_adapter_common(hdd_adapter_t *pAdapter)
{
if (NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: pAdapter is NULL ", __func__);
return;
}
init_completion(&pAdapter->session_open_comp_var);
init_completion(&pAdapter->session_close_comp_var);
init_completion(&pAdapter->disconnect_comp_var);
init_completion(&pAdapter->linkup_event_var);
init_completion(&pAdapter->cancel_rem_on_chan_var);
init_completion(&pAdapter->rem_on_chan_ready_event);
init_completion(&pAdapter->pno_comp_var);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
init_completion(&pAdapter->offchannel_tx_event);
#endif
init_completion(&pAdapter->tx_action_cnf_event);
#ifdef FEATURE_WLAN_TDLS
init_completion(&pAdapter->tdls_add_station_comp);
init_completion(&pAdapter->tdls_del_station_comp);
init_completion(&pAdapter->tdls_mgmt_comp);
init_completion(&pAdapter->tdls_link_establish_req_comp);
#endif
#ifdef WLAN_FEATURE_RMC
init_completion(&pAdapter->ibss_peer_info_comp);
#endif /* WLAN_FEATURE_RMC */
init_completion(&pAdapter->ula_complete);
init_completion(&pAdapter->change_country_code);
#ifdef FEATURE_WLAN_BATCH_SCAN
init_completion(&pAdapter->hdd_set_batch_scan_req_var);
init_completion(&pAdapter->hdd_get_batch_scan_req_var);
#endif
init_completion(&pAdapter->wlan_suspend_comp_var);
return;
}
#ifdef MDNS_OFFLOAD
/**
* hdd_mdns_enable_offload_done() - mdns enable offload response api
* @padapter: holds adapter
* @status: response status
*
* Return - None
*/
void hdd_mdns_enable_offload_done(void *padapter, VOS_STATUS status)
{
hdd_adapter_t* adapter = (hdd_adapter_t*) padapter;
ENTER();
if (NULL == adapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: adapter is NULL",__func__);
return;
}
adapter->mdns_status.mdns_enable_status = status;
vos_event_set(&adapter->mdns_status.vos_event);
return;
}
/**
* hdd_mdns_fqdn_offload_done() - mdns fqdn offload response api
* @padapter: holds adapter
* @status: responce status
*
* Return - None
*/
void hdd_mdns_fqdn_offload_done(void *padapter, VOS_STATUS status)
{
hdd_adapter_t* adapter = (hdd_adapter_t*) padapter;
ENTER();
if (NULL == adapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: adapter is NULL",__func__);
return;
}
adapter->mdns_status.mdns_fqdn_status = status;
return;
}
/**
* hdd_mdns_resp_offload_done() - mdns resp offload response api
* @padapter: holds adapter
* @status: responce status
*
* Return - None
*/
void hdd_mdns_resp_offload_done(void *padapter, VOS_STATUS status)
{
hdd_adapter_t* adapter = (hdd_adapter_t*) padapter;
ENTER();
if (NULL == adapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: adapter is NULL",__func__);
return;
}
adapter->mdns_status.mdns_resp_status = status;
return;
}
/**
* wlan_hdd_mdns_process_response_dname() - Process mDNS domain name
* @response: Pointer to a struct hdd_mdns_resp_info
* @resp_info: Pointer to a struct tSirMDNSResponseInfo
*
* This function will pack the whole domain name without compression. It will
* add the leading len for each field and add zero length octet to terminate
* the domain name.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_process_response_dname(struct hdd_mdns_resp_info *response,
sir_mdns_resp_info resp_info)
{
uint8_t num;
uint16_t idx;
uint8_t len = 0;
if ((response == NULL) || (response->data == NULL) ||
(response->offset == NULL)) {
hddLog(LOGE, FL("Either data or offset in response is NULL!"));
return FALSE;
}
if ((resp_info == NULL) ||
(resp_info->resp_len >= MAX_MDNS_RESP_LEN)) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
for (num = 0; num < response->num_entries; num++) {
response->offset[num] =
resp_info->resp_len + MDNS_HEADER_LEN;
idx = num * MAX_LEN_DOMAINNAME_FIELD;
len = strlen((char *)&response->data[idx]);
if ((resp_info->resp_len + len + 1) >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"),
MAX_MDNS_RESP_LEN);
return FALSE;
}
resp_info->resp_data[resp_info->resp_len] = len;
resp_info->resp_len++;
vos_mem_copy(&resp_info->resp_data[resp_info->resp_len],
&response->data[idx], len);
resp_info->resp_len += len;
}
/* The domain name terminates with the zero length octet */
if (num == response->num_entries) {
if (resp_info->resp_len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"),
MAX_MDNS_RESP_LEN);
return FALSE;
}
resp_info->resp_data[resp_info->resp_len] = 0;
resp_info->resp_len++;
}
return TRUE;
}
/**
* wlan_hdd_mdns_format_response_u16() - Form uint16_t response data
* @value: The uint16_t value is formed to the struct tSirMDNSResponseInfo
* @resp_info: Pointer to a struct tSirMDNSResponseInfo
*
* Return: None
*/
static void wlan_hdd_mdns_format_response_u16(uint16_t value,
sir_mdns_resp_info resp_info)
{
uint8_t val_u8;
if ((resp_info == NULL) || (resp_info->resp_data == NULL))
return;
val_u8 = (value & 0xff00) >> 8;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
val_u8 = value & 0xff;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
}
/**
* wlan_hdd_mdns_format_response_u32() - Form uint32_t response data
* @value: The uint32_t value is formed to the struct tSirMDNSResponseInfo
* @resp_info: Pointer to a struct tSirMDNSResponseInfo
*
* Return: None
*/
static void wlan_hdd_mdns_format_response_u32(uint32_t value,
sir_mdns_resp_info resp_info)
{
uint8_t val_u8;
if ((resp_info == NULL) || (resp_info->resp_data == NULL))
return;
val_u8 = (value & 0xff000000) >> 24;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
val_u8 = (value & 0xff0000) >> 16;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
val_u8 = (value & 0xff00) >> 8;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
val_u8 = value & 0xff;
resp_info->resp_data[resp_info->resp_len++] = val_u8;
}
/**
* wlan_hdd_mdns_process_response_misc() - Process misc info in mDNS response
* @resp_type: Response type for mDNS
* @resp_info: Pointer to a struct tSirMDNSResponseInfo
*
* This function will pack the response type, class and TTL (Time To Live).
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool wlan_hdd_mdns_process_response_misc(uint16_t resp_type,
sir_mdns_resp_info resp_info)
{
uint16_t len;
if (resp_info == NULL) {
hddLog(LOGE, FL("resp_info is NULL!"));
return FALSE;
}
len = resp_info->resp_len + (2 * sizeof(uint16_t) + sizeof(uint32_t));
if (len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
/* Fill Type, Class, TTL */
wlan_hdd_mdns_format_response_u16(resp_type, resp_info);
wlan_hdd_mdns_format_response_u16(MDNS_CLASS, resp_info);
wlan_hdd_mdns_format_response_u32(MDNS_TTL, resp_info);
return TRUE;
}
/**
* wlan_hdd_mdns_compress_data() - Compress the domain name in mDNS response
* @resp_info: Pointer to a struct tSirMDNSResponseInfo
* @response_dst: The response which domain name is compressed.
* @response_src: The response which domain name is matched with response_dst.
* Its offset is used for data compression.
* @num_matched: The number of matched entries between response_dst and
* response_src
*
* This function will form the different fields of domain name in response_dst
* if any. Then use the offset of the matched domain name in response_src to
* compress the matched domain name.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_compress_data(sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *response_dst,
struct hdd_mdns_resp_info *response_src,
uint8_t num_matched)
{
uint8_t num, num_diff;
uint16_t value, idx;
uint8_t len = 0;
if ((response_src == NULL) || (response_dst == NULL) ||
(resp_info == NULL)) {
hddLog(LOGE, FL("response info is NULL!"));
return FALSE;
}
if (response_dst->num_entries < num_matched) {
hddLog(LOGE, FL("num_entries is less than num_matched!"));
return FALSE;
}
if (resp_info->resp_len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
num_diff = response_dst->num_entries - num_matched;
if ((num_diff > 0) && (response_dst->data == NULL)) {
hddLog(LOGE, FL("response_dst->data is NULL!"));
return FALSE;
}
/*
* Handle the unmatched string at the beginning
* Store the length of octets and the octets
*/
for (num = 0; num < num_diff; num++) {
response_dst->offset[num] =
resp_info->resp_len + MDNS_HEADER_LEN;
idx = num * MAX_LEN_DOMAINNAME_FIELD;
len = strlen((char *)&response_dst->data[idx]);
if ((resp_info->resp_len + len + 1) >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"),
MAX_MDNS_RESP_LEN);
return FALSE;
}
resp_info->resp_data[resp_info->resp_len] = len;
resp_info->resp_len++;
vos_mem_copy(&resp_info->resp_data[resp_info->resp_len],
&response_dst->data[idx], len);
resp_info->resp_len += len;
}
/*
* Handle the matched string from the end
* Just keep the offset and mask the leading two bit
*/
if (response_src->num_entries >= num_matched) {
num_diff = response_src->num_entries - num_matched;
value = response_src->offset[num_diff];
if (value > 0) {
value |= 0xc000;
if ((resp_info->resp_len + sizeof(uint16_t)) >=
MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"),
MAX_MDNS_RESP_LEN);
return FALSE;
}
wlan_hdd_mdns_format_response_u16(value, resp_info);
return TRUE;
}
}
return FALSE;
}
/**
* wlan_hdd_mdns_reset_response() - Reset the response info
* @response: The response which info is reset.
*
* Return: None
*/
static void wlan_hdd_mdns_reset_response(struct hdd_mdns_resp_info *response)
{
if (response == NULL)
return;
response->num_entries = 0;
response->data = NULL;
response->offset = NULL;
}
/**
* wlan_hdd_mdns_init_response() - Initialize the response info
* @response: The response which info is initiatized.
* @resp_dname: The domain name string which might be tokenized.
*
* This function will allocate the memory for both response->data and
* response->offset. Besides, it will also tokenize the domain name to some
* entries and fill response->num_entries with the num of entries.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool wlan_hdd_mdns_init_response(struct hdd_mdns_resp_info *response,
uint8_t *resp_dname, char separator)
{
uint16_t size;
if ((resp_dname == NULL) || (response == NULL)) {
hddLog(LOGE, FL("resp_dname or response is NULL!"));
return FALSE;
}
size = MAX_NUM_FIELD_DOMAINNAME * MAX_LEN_DOMAINNAME_FIELD;
response->data = vos_mem_malloc(size);
if (response->data) {
vos_mem_zero(response->data, size);
if (VOS_STATUS_SUCCESS !=
hdd_string_to_string_array((char *)resp_dname,
response->data,
separator,
&response->num_entries,
MAX_NUM_FIELD_DOMAINNAME,
MAX_LEN_DOMAINNAME_FIELD)) {
hddLog(LOGE, FL("hdd_string_to_string_array fail!"));
goto err_init_resp;
}
if ((response->num_entries > 0) &&
(strlen((char *)&response->data[0]) > 0)) {
size = sizeof(uint16_t) * response->num_entries;
response->offset = vos_mem_malloc(size);
if (response->offset) {
vos_mem_zero(response->offset, size);
return TRUE;
}
}
}
err_init_resp:
if (response->data)
vos_mem_free(response->data);
wlan_hdd_mdns_reset_response(response);
return FALSE;
}
/**
* wlan_hdd_mdns_find_entries_from_end() - Find the matched entries
* @response1: The response info is used to be compared.
* @response2: The response info is used to be compared.
*
* This function will find the matched entries from the end.
*
* Return: Return the number of the matched entries.
*/
static uint8_t
wlan_hdd_mdns_find_entries_from_end(struct hdd_mdns_resp_info *response1,
struct hdd_mdns_resp_info *response2)
{
uint8_t min, len1, i;
uint16_t num1, num2;
uint8_t num_matched = 0;
min = VOS_MIN(response1->num_entries, response2->num_entries);
for (i = 1; i <= min; i++) {
num1 = (response1->num_entries - i);
num1 *= MAX_LEN_DOMAINNAME_FIELD;
num2 = (response2->num_entries - i);
num2 *= MAX_LEN_DOMAINNAME_FIELD;
len1 = strlen((char *)&response1->data[num1]);
if ((len1 == 0) ||
(len1 != strlen((char *)&response2->data[num2])))
break;
if (memcmp(&response1->data[num1],
&response2->data[num2], len1))
break;
else
num_matched++;
}
return num_matched;
}
/**
* wlan_hdd_mdns_find_max() - Find the maximum number of the matched entries
* @matchedlist: Pointer to the array of struct hdd_mdns_resp_matched
* @numlist: The number of the elements in the array matchedlist.
*
* Find the max number of the matched entries among the array matchedlist.
*
* Return: None
*/
static void wlan_hdd_mdns_find_max(struct hdd_mdns_resp_matched *matchedlist,
uint8_t numlist)
{
int j;
struct hdd_mdns_resp_matched tmp;
/* At least two values are used for sorting */
if ((numlist < 2) || (matchedlist == NULL)) {
hddLog(LOGE, FL("At least two values are used for sorting!"));
return;
}
for (j = 0; j < numlist-1; j++) {
if (matchedlist[j].num_matched >
matchedlist[j+1].num_matched) {
vos_mem_copy(&tmp, &matchedlist[j],
sizeof(struct hdd_mdns_resp_matched));
vos_mem_copy(&matchedlist[j], &matchedlist[j+1],
sizeof(struct hdd_mdns_resp_matched));
vos_mem_copy(&matchedlist[j+1], &tmp,
sizeof(struct hdd_mdns_resp_matched));
}
}
}
/**
* wlan_hdd_mdns_pack_response_type_a() - Pack Type A response
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* Type A response include QName, response type, class, TTL and Ipv4.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_a(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_a)
{
uint16_t value;
uint32_t len;
ENTER();
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_a == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type A response */
if (strlen((char *)ini_config->mdns_resp_type_a) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_a,
ini_config->mdns_resp_type_a, '.'))
return TRUE;
/* Process response domain name */
if (!wlan_hdd_mdns_process_response_dname(resptype_a, resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_A);
return FALSE;
}
/* Process response Type, Class, TTL */
if (!wlan_hdd_mdns_process_response_misc(MDNS_TYPE_A, resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS misc response (%d)!"),
MDNS_TYPE_A);
return FALSE;
}
/* Process response RDLength, RData */
len = sizeof(uint16_t) + sizeof(uint32_t);
len += resp_info->resp_len;
if (len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
value = sizeof(uint32_t);
wlan_hdd_mdns_format_response_u16(value, resp_info);
wlan_hdd_mdns_format_response_u32(ini_config->mdns_resp_type_a_ipv4,
resp_info);
EXIT();
return TRUE;
}
/**
* wlan_hdd_mdns_pack_response_type_txt() - Pack Type Txt response
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_txt: Pointer to the struct hdd_mdns_resp_info of Type txt
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* Type Txt response include QName, response type, class, TTL and text content.
* Also, it will find the matched QName from resptype_A and compress the data.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_txt(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_txt,
struct hdd_mdns_resp_info *resptype_a)
{
uint8_t num_matched;
uint8_t num;
uint16_t idx;
uint16_t value = 0;
uint32_t len;
uint32_t total_len;
bool status;
struct hdd_mdns_resp_info resptype_content;
ENTER();
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_txt == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type Txt response */
if (strlen((char *)ini_config->mdns_resp_type_txt) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_txt,
ini_config->mdns_resp_type_txt, '.'))
return TRUE;
/*
* For data compression
* Check if any strings are matched with Type A response
*/
if (resptype_a && (resptype_a->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_txt,
resptype_a);
if (num_matched > 0) {
if (!wlan_hdd_mdns_compress_data(resp_info,
resptype_txt, resptype_a, num_matched)) {
hddLog(LOGE, FL("Fail to compress mDNS "
"response (%d)!"), MDNS_TYPE_TXT);
return FALSE;
}
} else {
/*
* num_matched is zero. Error!
* At least ".local" is needed.
*/
hddLog(LOGE, FL("No matched string! Fail to pack mDNS "
"response (%d)!"), MDNS_TYPE_TXT);
return FALSE;
}
} else {
/* no TypeA response, so show the whole data */
if (!wlan_hdd_mdns_process_response_dname(resptype_txt,
resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_TXT);
return FALSE;
}
}
/* Process response Type, Class, TTL */
if (!wlan_hdd_mdns_process_response_misc(MDNS_TYPE_TXT, resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS misc response (%d)!"),
MDNS_TYPE_TXT);
return FALSE;
}
/*
* Process response RDLength, RData.
* TypeTxt RData include len.
*/
status = wlan_hdd_mdns_init_response(&resptype_content,
ini_config->mdns_resp_type_txt_content,
'/');
if (status == FALSE) {
hddLog(LOGE, FL("wlan_hdd_mdns_init_response FAIL"));
return FALSE;
}
for (num = 0; num < resptype_content.num_entries; num++) {
idx = num * MAX_LEN_DOMAINNAME_FIELD;
value += strlen((char *)&resptype_content.data[idx]);
}
/* content len is uint16_t */
total_len = sizeof(uint16_t);
total_len += resp_info->resp_len + value +
resptype_content.num_entries;
if (total_len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
wlan_hdd_mdns_format_response_u16(value + resptype_content.num_entries,
resp_info);
for (num = 0; num < resptype_content.num_entries; num++) {
idx = num * MAX_LEN_DOMAINNAME_FIELD;
len = strlen((char *)&resptype_content.data[idx]);
resp_info->resp_data[resp_info->resp_len] = len;
resp_info->resp_len++;
vos_mem_copy(&resp_info->resp_data[resp_info->resp_len],
&resptype_content.data[idx], len);
resp_info->resp_len += len;
hddLog(LOG1, FL("index = %d, len = %d, str = %s"),
num, len, &resptype_content.data[idx]);
}
EXIT();
return TRUE;
}
/**
* wlan_hdd_mdns_pack_response_type_ptr_dname() - Pack Type PTR domain name
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_ptr_dn: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* domain name
* @resptype_ptr: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* @resptype_txt: Pointer to the struct hdd_mdns_resp_info of Type Txt
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* The Type Ptr response include Type PTR domain name in its data field.
* Also, it will find the matched QName from the existing resptype_ptr,
* resptype_txt, resptype_a and then compress the data.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_ptr_dname(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_ptr_dn,
struct hdd_mdns_resp_info *resptype_ptr,
struct hdd_mdns_resp_info *resptype_txt,
struct hdd_mdns_resp_info *resptype_a)
{
uint8_t num_matched, numlist, size;
struct hdd_mdns_resp_matched matchedlist[MAX_MDNS_RESP_TYPE-1];
struct hdd_mdns_resp_info *resp;
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_ptr == NULL) || (resptype_ptr_dn == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type Ptr domain name response */
if (strlen((char *)ini_config->mdns_resp_type_ptr_dname) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_ptr_dn,
ini_config->mdns_resp_type_ptr_dname, '.'))
return TRUE;
/*
* For data compression
* Check if any strings are matched with previous
* response.
*/
numlist = 0;
size = (MAX_MDNS_RESP_TYPE-1);
size *= sizeof(struct hdd_mdns_resp_matched);
vos_mem_zero(matchedlist, size);
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_ptr_dn,
resptype_ptr);
if (num_matched > 0) {
matchedlist[numlist].num_matched = num_matched;
matchedlist[numlist].type = MDNS_TYPE_PTR;
numlist++;
}
if (resptype_txt && (resptype_txt->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_ptr_dn, resptype_txt);
if (num_matched > 0) {
matchedlist[numlist].num_matched = num_matched;
matchedlist[numlist].type = MDNS_TYPE_TXT;
numlist++;
}
}
if (resptype_a && (resptype_a->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_ptr_dn,resptype_a);
if (num_matched > 0) {
matchedlist[numlist].num_matched = num_matched;
matchedlist[numlist].type = MDNS_TYPE_A;
numlist++;
}
}
if (numlist > 0) {
if (numlist > 1)
wlan_hdd_mdns_find_max(matchedlist, numlist);
resp = NULL;
switch (matchedlist[numlist-1].type) {
case MDNS_TYPE_A:
resp = resptype_a;
break;
case MDNS_TYPE_TXT:
resp = resptype_txt;
break;
case MDNS_TYPE_PTR:
resp = resptype_ptr;
break;
default:
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_PTR_DNAME);
return FALSE;
}
num_matched = matchedlist[numlist-1].num_matched;
if (!wlan_hdd_mdns_compress_data(resp_info, resptype_ptr_dn,
resp, num_matched)) {
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_PTR_DNAME);
return FALSE;
}
} else {
/* num = 0 -> no matched string */
if (!wlan_hdd_mdns_process_response_dname(resptype_ptr_dn,
resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_PTR_DNAME);
return FALSE;
}
}
return TRUE;
}
/**
* wlan_hdd_mdns_pack_response_type_ptr() - Pack Type PTR response
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_ptr: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* @resptype_ptr_dn: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* domain name
* @resptype_txt: Pointer to the struct hdd_mdns_resp_info of Type Txt
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* The Type Ptr response include QName, response type, class, TTL and
* Type PTR domain name. Also, it will find the matched QName from the
* existing resptype_txt, resptype_a and then compress the data.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_ptr(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_ptr,
struct hdd_mdns_resp_info *resptype_ptr_dn,
struct hdd_mdns_resp_info *resptype_txt,
struct hdd_mdns_resp_info *resptype_a)
{
uint8_t num_matched, num_matched1;
uint16_t value;
uint8_t val_u8;
uint32_t offset_data_len, len;
ENTER();
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_ptr == NULL) || (resptype_ptr_dn == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type Ptr response */
if (strlen((char *)ini_config->mdns_resp_type_ptr) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_ptr,
ini_config->mdns_resp_type_ptr, '.'))
return TRUE;
/*
* For data compression
* Check if any strings are matched with Type A response
*/
num_matched = 0;
num_matched1 = 0;
if (resptype_a && (resptype_a->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_ptr,
resptype_a);
}
if (resptype_txt && (resptype_txt->num_entries > 0)) {
num_matched1 = wlan_hdd_mdns_find_entries_from_end(
resptype_ptr, resptype_txt);
}
if ((num_matched != num_matched1) ||
((num_matched > 0) && (num_matched1 > 0))) {
if (num_matched >= num_matched1) {
if (!wlan_hdd_mdns_compress_data(resp_info,
resptype_ptr, resptype_a, num_matched)) {
hddLog(LOGE, FL("Fail to compress mDNS "
"response (%d)!"), MDNS_TYPE_PTR);
return FALSE;
}
} else {
/* num_matched is less than num_matched1 */
if (!wlan_hdd_mdns_compress_data(resp_info,
resptype_ptr, resptype_txt, num_matched1)) {
hddLog(LOGE, FL("Fail to compress mDNS "
"response (%d)!"), MDNS_TYPE_PTR);
return FALSE;
}
}
} else {
/*
* Both num_matched and num_matched1 are zero.
* no TypeA & TypeTxt
*/
if (!wlan_hdd_mdns_process_response_dname(resptype_ptr,
resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_PTR);
return FALSE;
}
}
/* Process response Type, Class, TTL */
if (!wlan_hdd_mdns_process_response_misc(MDNS_TYPE_PTR, resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS misc response (%d)!"),
MDNS_TYPE_PTR);
return FALSE;
}
/*
* Process response RDLength, RData (Ptr domain name)
* Save the offset of RData length
*/
offset_data_len = resp_info->resp_len;
resp_info->resp_len += sizeof(uint16_t);
if (!wlan_hdd_mdns_pack_response_type_ptr_dname(ini_config, resp_info,
resptype_ptr_dn, resptype_ptr,
resptype_txt, resptype_a)) {
return FALSE;
}
/* Set the RData length */
len = offset_data_len + sizeof(uint16_t);
if ((resptype_ptr_dn->num_entries > 0) &&
(resp_info->resp_len > len)) {
value = resp_info->resp_len - len;
val_u8 = (value & 0xff00) >> 8;
resp_info->resp_data[offset_data_len] = val_u8;
val_u8 = value & 0xff;
resp_info->resp_data[offset_data_len+1] = val_u8;
} else {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_PTR);
return FALSE;
}
EXIT();
return TRUE;
}
/**
* wlan_hdd_mdns_pack_response_type_srv_target()- Pack Type Service Target
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_srv_tgt: Pointer to the struct hdd_mdns_resp_info of Type Srv
* target
* @resptype_srv: Pointer to the struct hdd_mdns_resp_info of Type Srv
* @resptype_ptr: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* @resptype_ptr_dn: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* domain name
* @resptype_txt: Pointer to the struct hdd_mdns_resp_info of Type Txt
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* The Type service target is one of the data field in the Type SRV response.
* Also, it will find the matched QName from the existing resptype_srv,
* resptype_ptr, resptype_ptr_dn, resptype_txt, resptype_a and then compress
* the data.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_srv_target(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_srv_tgt,
struct hdd_mdns_resp_info *resptype_srv,
struct hdd_mdns_resp_info *resptype_ptr,
struct hdd_mdns_resp_info *resptype_ptr_dn,
struct hdd_mdns_resp_info *resptype_txt,
struct hdd_mdns_resp_info *resptype_a)
{
uint8_t num_matched, num, size;
struct hdd_mdns_resp_matched matchedlist[MAX_MDNS_RESP_TYPE-1];
struct hdd_mdns_resp_info *resp;
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_srv == NULL) || (resptype_srv_tgt == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type Srv Target response */
if (strlen((char *)ini_config->mdns_resp_type_srv_target) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_srv_tgt,
ini_config->mdns_resp_type_srv_target, '.'))
return TRUE;
/*
* For data compression
* Check if any strings are matched with previous response.
*/
num = 0;
size = (MAX_MDNS_RESP_TYPE-1);
size *= sizeof(struct hdd_mdns_resp_matched);
vos_mem_zero(matchedlist, size);
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_srv_tgt,
resptype_srv);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_SRV;
num++;
}
if (resptype_ptr && (resptype_ptr->num_entries > 0)) {
if (resptype_ptr_dn && (resptype_ptr_dn->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_srv_tgt, resptype_ptr_dn);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_PTR_DNAME;
num++;
}
}
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_srv_tgt, resptype_ptr);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_PTR;
num++;
}
}
if (resptype_txt && (resptype_txt->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_srv_tgt, resptype_txt);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_TXT;
num++;
}
}
if (resptype_a && (resptype_a->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_srv_tgt, resptype_a);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_A;
num++;
}
}
if (num > 0) {
if (num > 1)
wlan_hdd_mdns_find_max(matchedlist, num);
resp = NULL;
switch (matchedlist[num-1].type) {
case MDNS_TYPE_A:
resp = resptype_a;
break;
case MDNS_TYPE_TXT:
resp = resptype_txt;
break;
case MDNS_TYPE_PTR:
resp = resptype_ptr;
break;
case MDNS_TYPE_PTR_DNAME:
resp = resptype_ptr_dn;
break;
case MDNS_TYPE_SRV:
resp = resptype_srv;
break;
default:
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_SRV_TARGET);
return FALSE;
}
num_matched = matchedlist[num-1].num_matched;
if (!wlan_hdd_mdns_compress_data(resp_info, resptype_srv_tgt,
resp, num_matched)) {
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_SRV_TARGET);
return FALSE;
}
} else {
/* num = 0 -> no matched string */
if (!wlan_hdd_mdns_process_response_dname(resptype_srv_tgt,
resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_SRV_TARGET);
return FALSE;
}
}
return TRUE;
}
/**
* wlan_hdd_mdns_pack_response_type_srv()- Pack Type Service response
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
* @resptype_srv: Pointer to the struct hdd_mdns_resp_info of Type Srv
* @resptype_srv_tgt: Pointer to the struct hdd_mdns_resp_info of Type Srv
* target
* @resptype_ptr: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* @resptype_ptr_dn: Pointer to the struct hdd_mdns_resp_info of Type Ptr
* domain name
* @resptype_txt: Pointer to the struct hdd_mdns_resp_info of Type Txt
* @resptype_a: Pointer to the struct hdd_mdns_resp_info of Type A
*
* The Type SRV (Service) response include QName, response type, class, TTL
* and four kinds of data fields. Also, it will find the matched QName from
* the existing resptype_ptr, resptype_ptr_dn, resptype_txt, resptype_a and
* then compress the data.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool
wlan_hdd_mdns_pack_response_type_srv(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info,
struct hdd_mdns_resp_info *resptype_srv,
struct hdd_mdns_resp_info *resptype_srv_tgt,
struct hdd_mdns_resp_info *resptype_ptr,
struct hdd_mdns_resp_info *resptype_ptr_dn,
struct hdd_mdns_resp_info *resptype_txt,
struct hdd_mdns_resp_info *resptype_a)
{
uint8_t num_matched, num, size;
uint16_t value;
uint8_t val_u8;
uint32_t offset_data_len, len;
struct hdd_mdns_resp_info *resp;
struct hdd_mdns_resp_matched matchedlist[MAX_MDNS_RESP_TYPE-1];
ENTER();
if ((ini_config == NULL) || (resp_info == NULL) ||
(resptype_srv == NULL) || (resptype_srv_tgt == NULL)) {
hddLog(LOGE, FL("ini_config or response info is NULL!"));
return FALSE;
}
/* No Type Srv response */
if (strlen((char *)ini_config->mdns_resp_type_srv) <= 0)
return TRUE;
/* Wrong response is assigned, just ignore this response */
if (!wlan_hdd_mdns_init_response(resptype_srv,
ini_config->mdns_resp_type_srv, '.'))
return TRUE;
/*
* For data compression
* Check if any strings are matched with Type A response
*/
num = 0;
size = (MAX_MDNS_RESP_TYPE-1);
size *= sizeof(struct hdd_mdns_resp_matched);
vos_mem_zero(matchedlist, size);
if (resptype_ptr && (resptype_ptr->num_entries > 0)) {
if (resptype_ptr_dn && (resptype_ptr_dn->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(
resptype_srv,
resptype_ptr_dn);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_PTR_DNAME;
num++;
}
}
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_srv,
resptype_ptr);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_PTR;
num++;
}
}
if (resptype_txt && (resptype_txt->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_srv,
resptype_txt);
if (num_matched > 0) {
matchedlist[num].num_matched =num_matched;
matchedlist[num].type = MDNS_TYPE_TXT;
num++;
}
}
if (resptype_a && (resptype_a->num_entries > 0)) {
num_matched = wlan_hdd_mdns_find_entries_from_end(resptype_srv,
resptype_a);
if (num_matched > 0) {
matchedlist[num].num_matched = num_matched;
matchedlist[num].type = MDNS_TYPE_A;
num++;
}
}
if (num > 0) {
if (num > 1)
wlan_hdd_mdns_find_max(matchedlist, num);
resp = NULL;
switch (matchedlist[num-1].type) {
case MDNS_TYPE_A:
resp = resptype_a;
break;
case MDNS_TYPE_TXT:
resp = resptype_txt;
break;
case MDNS_TYPE_PTR:
resp = resptype_ptr;
break;
case MDNS_TYPE_PTR_DNAME:
resp = resptype_ptr_dn;
break;
default:
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_SRV);
return FALSE;
}
num_matched = matchedlist[num-1].num_matched;
if (!wlan_hdd_mdns_compress_data(resp_info, resptype_srv,
resp, num_matched)) {
hddLog(LOGE, FL("Fail to compress mDNS response "
"(%d)!"), MDNS_TYPE_SRV);
return FALSE;
}
} else {
/* num = 0 -> no matched string */
if (!wlan_hdd_mdns_process_response_dname(resptype_srv,
resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_SRV);
return FALSE;
}
}
/* Process response Type, Class, TTL */
if (!wlan_hdd_mdns_process_response_misc(MDNS_TYPE_SRV, resp_info)) {
hddLog(LOGE, FL("Fail to process mDNS misc response (%d)!"),
MDNS_TYPE_SRV);
return FALSE;
}
/*
* Process response RDLength, RData (Srv target name)
* Save the offset of RData length
*/
offset_data_len = resp_info->resp_len;
resp_info->resp_len += sizeof(uint16_t);
len = resp_info->resp_len + (3 * sizeof(uint16_t));
if (len >= MAX_MDNS_RESP_LEN) {
hddLog(LOGE, FL("resp_len exceeds %d!"), MAX_MDNS_RESP_LEN);
return FALSE;
}
/* set Srv Priority */
value = ini_config->mdns_resp_type_srv_priority;
wlan_hdd_mdns_format_response_u16(value, resp_info);
/* set Srv Weight */
value = ini_config->mdns_resp_type_srv_weight;
wlan_hdd_mdns_format_response_u16(value, resp_info);
/* set Srv Port */
value = ini_config->mdns_resp_type_srv_port;
wlan_hdd_mdns_format_response_u16(value, resp_info);
if (!wlan_hdd_mdns_pack_response_type_srv_target(ini_config, resp_info,
resptype_srv_tgt, resptype_srv,
resptype_ptr, resptype_ptr_dn,
resptype_txt, resptype_a)) {
return FALSE;
}
/* Set the RData length */
len = offset_data_len + sizeof(uint16_t);
if ((resptype_srv_tgt->num_entries > 0) &&
(resp_info->resp_len > len)) {
value = resp_info->resp_len - len;
val_u8 = (value & 0xff00) >> 8;
resp_info->resp_data[offset_data_len] = val_u8;
val_u8 = value & 0xff;
resp_info->resp_data[offset_data_len+1] = val_u8;
} else {
hddLog(LOGE, FL("Fail to process mDNS response (%d)!"),
MDNS_TYPE_SRV);
return FALSE;
}
EXIT();
return TRUE;
}
/**
* wlan_hdd_mdns_free_mem() - Free the allocated memory
* @response: Pointer to the struct hdd_mdns_resp_info
*
* Return: None
*/
static void wlan_hdd_mdns_free_mem(struct hdd_mdns_resp_info *response)
{
if (response && response->data)
vos_mem_free(response->data);
if (response && response->offset)
vos_mem_free(response->offset);
}
/**
* wlan_hdd_mdns_pack_response() - Pack mDNS response
* @ini_config: Pointer to the struct hdd_config_t
* @resp_info: Pointer to the struct tSirMDNSResponseInfo
*
* This function will pack four types of responses (Type A, Type Txt, Type Ptr
* and Type Service). Each response contains QName, response type, class, TTL
* and data fields.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
static bool wlan_hdd_mdns_pack_response(hdd_config_t *ini_config,
sir_mdns_resp_info resp_info)
{
struct hdd_mdns_resp_info resptype_a, resptype_txt;
struct hdd_mdns_resp_info resptype_ptr, resptype_ptr_dn;
struct hdd_mdns_resp_info resptype_srv, resptype_srv_tgt;
uint32_t num_res_records = 0;
bool status = FALSE;
ENTER();
wlan_hdd_mdns_reset_response(&resptype_a);
wlan_hdd_mdns_reset_response(&resptype_txt);
wlan_hdd_mdns_reset_response(&resptype_ptr);
wlan_hdd_mdns_reset_response(&resptype_ptr_dn);
wlan_hdd_mdns_reset_response(&resptype_srv);
wlan_hdd_mdns_reset_response(&resptype_srv_tgt);
resp_info->resp_len = 0;
/* Process Type A response */
if (!wlan_hdd_mdns_pack_response_type_a(ini_config, resp_info,
&resptype_a))
goto err_resptype_a;
if ((resptype_a.num_entries > 0) &&
(strlen((char *)&resptype_a.data[0]) > 0))
num_res_records++;
/* Process Type TXT response */
if (!wlan_hdd_mdns_pack_response_type_txt(ini_config, resp_info,
&resptype_txt, &resptype_a))
goto err_resptype_txt;
if ((resptype_txt.num_entries > 0) &&
(strlen((char *)&resptype_txt.data[0]) > 0))
num_res_records++;
/* Process Type PTR response */
if (!wlan_hdd_mdns_pack_response_type_ptr(ini_config, resp_info,
&resptype_ptr, &resptype_ptr_dn,
&resptype_txt, &resptype_a))
goto err_resptype_ptr;
if ((resptype_ptr.num_entries > 0) &&
(strlen((char *)&resptype_ptr.data[0]) > 0))
num_res_records++;
/* Process Type SRV response */
if (!wlan_hdd_mdns_pack_response_type_srv(ini_config, resp_info,
&resptype_srv, &resptype_srv_tgt,
&resptype_ptr, &resptype_ptr_dn,
&resptype_txt, &resptype_a))
goto err_resptype_srv;
if ((resptype_srv.num_entries > 0) &&
(strlen((char *)&resptype_srv.data[0]) > 0))
num_res_records++;
resp_info->resourceRecord_count = num_res_records;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Pack mDNS response data successfully!", __func__);
status = TRUE;
err_resptype_srv:
wlan_hdd_mdns_free_mem(&resptype_srv);
wlan_hdd_mdns_free_mem(&resptype_srv_tgt);
err_resptype_ptr:
wlan_hdd_mdns_free_mem(&resptype_ptr);
wlan_hdd_mdns_free_mem(&resptype_ptr_dn);
err_resptype_txt:
wlan_hdd_mdns_free_mem(&resptype_txt);
err_resptype_a:
wlan_hdd_mdns_free_mem(&resptype_a);
EXIT();
return status;
}
/**
* wlan_hdd_set_mdns_offload() - Enable mDNS offload
* @hostapd_adapter: Pointer to the struct hdd_adapter_t
*
* This function will set FQDN/unique FQDN (full qualified domain name)
* and the mDNS response. Then send them to SME.
*
* Return: Return boolean. TRUE for success, FALSE for fail.
*/
bool wlan_hdd_set_mdns_offload(hdd_adapter_t *hostapd_adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(hostapd_adapter);
sir_mdns_offload_info mdns_offload_info;
sir_mdns_fqdn_info mdns_fqdn_info;
sir_mdns_resp_info mdns_resp_info;
uint32_t fqdn_len, ufqdn_len;
ENTER();
/* 1. Prepare the MDNS fqdn request to send to SME */
fqdn_len = strlen(hdd_ctx->cfg_ini->mdns_fqdn);
ufqdn_len = strlen(hdd_ctx->cfg_ini->mdns_uniquefqdn);
if ((fqdn_len == 0) && (ufqdn_len == 0)) {
hddLog(LOGE, FL("No mDNS FQDN or UFQDN is assigned fqdn_len %d,"
"ufqdn_len %d!"), fqdn_len, ufqdn_len);
return FALSE;
}
mdns_fqdn_info = vos_mem_malloc(sizeof(*mdns_fqdn_info));
if (NULL == mdns_fqdn_info) {
hddLog(LOGE, FL("could not allocate tSirMDNSFqdnInfo!"));
return FALSE;
}
/* MDNS fqdn request */
if (fqdn_len > 0) {
vos_mem_zero(mdns_fqdn_info, sizeof(*mdns_fqdn_info));
mdns_fqdn_info->bss_idx = hostapd_adapter->sessionId;
mdns_fqdn_info->fqdn_type = MDNS_FQDN_TYPE_GENERAL;
mdns_fqdn_info->fqdn_len = fqdn_len;
mdns_fqdn_info->mdns_fqdn_callback = hdd_mdns_fqdn_offload_done;
mdns_fqdn_info->mdns_fqdn_cb_context = hostapd_adapter;
vos_mem_copy(mdns_fqdn_info->fqdn_data,
hdd_ctx->cfg_ini->mdns_fqdn,
mdns_fqdn_info->fqdn_len);
if (eHAL_STATUS_SUCCESS !=
sme_set_mdns_fqdn(hdd_ctx->hHal, mdns_fqdn_info)) {
hddLog(LOGE, FL("sme_set_mdns_fqdn fail!"));
vos_mem_free(mdns_fqdn_info);
return FALSE;
}
}
/* MDNS unique fqdn request */
if (ufqdn_len > 0) {
vos_mem_zero(mdns_fqdn_info, sizeof(*mdns_fqdn_info));
mdns_fqdn_info->bss_idx = hostapd_adapter->sessionId;
mdns_fqdn_info->fqdn_type = MDNS_FQDN_TYPE_UNIQUE;
mdns_fqdn_info->fqdn_len = ufqdn_len;
mdns_fqdn_info->mdns_fqdn_callback = hdd_mdns_fqdn_offload_done;
mdns_fqdn_info->mdns_fqdn_cb_context = hostapd_adapter;
vos_mem_copy(mdns_fqdn_info->fqdn_data,
hdd_ctx->cfg_ini->mdns_uniquefqdn,
mdns_fqdn_info->fqdn_len);
if (eHAL_STATUS_SUCCESS !=
sme_set_mdns_fqdn(hdd_ctx->hHal, mdns_fqdn_info)) {
hddLog(LOGE, FL("sme_set_mdns_fqdn fail!"));
vos_mem_free(mdns_fqdn_info);
return FALSE;
}
}
vos_mem_free(mdns_fqdn_info);
/* 2. Prepare the MDNS response request to send to SME */
mdns_resp_info = vos_mem_malloc(sizeof(*mdns_resp_info));
if (NULL == mdns_resp_info) {
hddLog(LOGE, FL("could not allocate tSirMDNSResponseInfo!"));
return FALSE;
}
vos_mem_zero(mdns_resp_info, sizeof(*mdns_resp_info));
mdns_resp_info->bss_idx = hostapd_adapter->sessionId;
mdns_resp_info->mdns_resp_callback = hdd_mdns_resp_offload_done;
mdns_resp_info->mdns_resp_cb_context = hostapd_adapter;
if (!wlan_hdd_mdns_pack_response(hdd_ctx->cfg_ini, mdns_resp_info)) {
hddLog(LOGE, FL("wlan_hdd_pack_mdns_response fail!"));
vos_mem_free(mdns_resp_info);
return FALSE;
}
if (eHAL_STATUS_SUCCESS !=
sme_set_mdns_resp(hdd_ctx->hHal, mdns_resp_info)) {
hddLog(LOGE, FL("sme_set_mdns_resp fail!"));
vos_mem_free(mdns_resp_info);
return FALSE;
}
vos_mem_free(mdns_resp_info);
/* 3. Prepare the MDNS Enable request to send to SME */
mdns_offload_info = vos_mem_malloc(sizeof(*mdns_offload_info));
if (NULL == mdns_offload_info) {
hddLog(LOGE, FL("could not allocate tSirMDNSOffloadInfo!"));
return FALSE;
}
vos_mem_zero(mdns_offload_info, sizeof(*mdns_offload_info));
mdns_offload_info->bss_idx = hostapd_adapter->sessionId;
mdns_offload_info->enable = hdd_ctx->cfg_ini->enable_mdns_offload;
mdns_offload_info->mdns_enable_callback = hdd_mdns_enable_offload_done;
mdns_offload_info->mdns_enable_cb_context = hostapd_adapter;
if (eHAL_STATUS_SUCCESS !=
sme_set_mdns_offload(hdd_ctx->hHal, mdns_offload_info)) {
hddLog(LOGE, FL("sme_set_mdns_offload fail!"));
vos_mem_free(mdns_offload_info);
return FALSE;
}
vos_mem_free(mdns_offload_info);
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: enable mDNS offload successfully!", __func__);
return TRUE;
}
#endif /* MDNS_OFFLOAD */
/**
* wlan_hdd_start_sap() - This function starts bss of SAP.
* @ap_adapter: SAP adapter
*
* This function will process the starting of sap adapter.
*
* Return: void.
*/
void wlan_hdd_start_sap(hdd_adapter_t *ap_adapter)
{
hdd_ap_ctx_t *hdd_ap_ctx;
hdd_hostapd_state_t *hostapd_state;
VOS_STATUS vos_status;
hdd_context_t *hdd_ctx;
tsap_Config_t *pConfig;
if (NULL == ap_adapter) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("ap_adapter is NULL here"));
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter);
hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter);
pConfig = &ap_adapter->sessionCtx.ap.sapConfig;
mutex_lock(&hdd_ctx->sap_lock);
if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags))
goto end;
if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) {
hddLog(LOGE, FL("SAP Not able to set AP IEs"));
goto end;
}
vos_event_reset(&hostapd_state->vosEvent);
if (WLANSAP_StartBss(hdd_ctx->pvosContext, hdd_hostapd_SAPEventCB,
&hdd_ap_ctx->sapConfig, (v_PVOID_t)ap_adapter->dev)
!= VOS_STATUS_SUCCESS) {
goto end;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
FL("Waiting for SAP to start"));
vos_status = vos_wait_single_event(&hostapd_state->vosEvent, 10000);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("SAP Start failed"));
goto end;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
FL("SAP Start Success"));
set_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags);
wlan_hdd_incr_active_session(hdd_ctx, ap_adapter->device_mode);
hostapd_state->bCommit = TRUE;
end:
mutex_unlock(&hdd_ctx->sap_lock);
return;
}
#ifdef WLAN_FEATURE_TSF
/**
* hdd_tsf_cb() - handle tsf request callback
*
* @pcb_cxt: pointer to the hdd_contex
* @ptsf: pointer to struct stsf
*
* Based on the request sent .
*
* Return: Describe the execute result of this routine
*/
static int hdd_tsf_cb(void *pcb_ctx, struct stsf *ptsf)
{
hdd_context_t *hddctx;
int status;
hdd_adapter_t* adapter = (hdd_adapter_t*)pcb_ctx;
if (pcb_ctx == NULL || ptsf == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("HDD context is not valid"));
return -EINVAL;
}
hddctx = (hdd_context_t *)pcb_ctx;
status = wlan_hdd_validate_context(hddctx);
if (0 != status)
return -EINVAL;
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("failed to find adapter"));
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("tsf cb handle event, device_mode is %d"),
adapter->device_mode);
/* copy the return value to hdd_tsf_ctx in adapter*/
if (ptsf->tsf_req_status) {
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_get_state = TSF_NOT_RETURNED_BY_FW;
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_event_set (&adapter->tsf_cap_ctx.tsf_capture_done_event);
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, FL("tsf req failure :%d"),
ptsf->tsf_req_status);
return ptsf->tsf_req_status;
}
/* If this is a get request.Store the tsf values in adapter. */
if (!ptsf->set_tsf_req) {
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_low = ptsf->tsf_low;
adapter->tsf_cap_ctx.tsf_high = ptsf->tsf_high;
adapter->tsf_cap_ctx.tsf_get_state = TSF_RETURN;
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
FL("hdd_get_tsf_cb sta=%u, tsf_low=%u, tsf_high=%u"),
adapter->sessionId, ptsf->tsf_low, ptsf->tsf_high);
}
else {
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_CAP_STATE;
adapter->tsf_cap_ctx.tsf_get_state = TSF_CURRENT_IN_CAP_STATE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
}
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
vos_event_set (&adapter->tsf_cap_ctx.tsf_capture_done_event);
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
/* free allocated mem */
vos_mem_free(ptsf);
return 0;
}
/**
* hdd_capture_tsf() - capture tsf
*
* @adapter: pointer to adapter
* @buf: pointer to upper layer buf
* @len : the length of buf
*
* This function returns tsf value to uplayer.
*
* Return: Describe the execute result of this routine
*/
int hdd_capture_tsf(hdd_adapter_t *adapter, uint32_t *buf, int len)
{
int ret = 0;
hdd_station_ctx_t *hdd_sta_ctx;
hdd_context_t *hdd_ctx;
tSirCapTsfParams cap_tsf_params;
VOS_STATUS status;
if (adapter == NULL || buf == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("invalid pointer"));
return -EINVAL;
}
if (len != 1)
return -EINVAL;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (wlan_hdd_validate_context(hdd_ctx)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("invalid hdd ctx"));
return -EINVAL;
}
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) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("failed to cap tsf, not connect with ap"));
buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
return ret;
}
}
if ((adapter->device_mode == WLAN_HDD_SOFTAP ||
adapter->device_mode == WLAN_HDD_P2P_GO) &&
!(test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Soft AP / P2p GO not beaconing"));
buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
return ret;
}
if (adapter->tsf_cap_ctx.tsf_capture_state == TSF_CAP_STATE) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("current in capture state, pls reset"));
buf[0] = TSF_CURRENT_IN_CAP_STATE;
} else {
hddLog(VOS_TRACE_LEVEL_INFO, FL("ioctl issue cap tsf cmd"));
buf[0] = TSF_RETURN;
cap_tsf_params.session_id = adapter->sessionId;
cap_tsf_params.tsf_rsp_cb_func = hdd_tsf_cb;
cap_tsf_params.tsf_rsp_cb_ctx = adapter;
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_CAP_STATE;
adapter->tsf_cap_ctx.tsf_get_state = TSF_CURRENT_IN_CAP_STATE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
ret = sme_capture_tsf_req(hdd_ctx->hHal, cap_tsf_params);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("capture fail"));
buf[0] = TSF_CAPTURE_FAIL;
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
return -EINVAL;
}
/* wait till we get a response from fw */
status = vos_wait_single_event(&adapter->tsf_cap_ctx.
tsf_capture_done_event,
HDD_TSF_CAP_REQ_TIMEOUT);
if (!VOS_IS_STATUS_SUCCESS(status)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("capture tsf vos wait for single_event failed!! %d"),
adapter->tsf_cap_ctx.tsf_get_state);
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
return -EINVAL;
}
}
buf[0] = TSF_RETURN;
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ioctl return cap tsf cmd, ret = %d"), ret);
return ret;
}
/**
* hdd_indicate_tsf() - return tsf to uplayer
*
* @adapter: pointer to adapter
* @buf: pointer to uplayer buf
* @len : the length of buf
*
* This function returns tsf value to uplayer.
*
* Return: Describe the execute result of this routine
*/
int hdd_indicate_tsf(hdd_adapter_t *adapter, uint32_t *buf, int len)
{
int ret = 0;
hdd_station_ctx_t *hdd_sta_ctx;
hdd_context_t *hdd_ctx;
tSirCapTsfParams cap_tsf_params;
VOS_STATUS status;
if (adapter == NULL || buf == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("invalid pointer"));
return -EINVAL;
}
if (len != 3)
return -EINVAL;
buf [1] = 0;
buf [2] = 0;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (wlan_hdd_validate_context(hdd_ctx)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("invalid hdd ctx"));
return -EINVAL;
}
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) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("failed to cap tsf, not connect with ap"));
buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
return ret;
}
}
if ((adapter->device_mode == WLAN_HDD_SOFTAP ||
adapter->device_mode == WLAN_HDD_P2P_GO) &&
!(test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Soft AP / P2p GO not beaconing"));
buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
return ret;
}
if (adapter->tsf_cap_ctx.tsf_capture_state != TSF_CAP_STATE ||
adapter->tsf_cap_ctx.tsf_get_state != TSF_CURRENT_IN_CAP_STATE ) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("Not in capture state,Enter capture state first"));
buf[0] = TSF_GET_FAIL;
} else {
hddLog(VOS_TRACE_LEVEL_INFO, FL("ioctl issue cap tsf cmd"));
cap_tsf_params.session_id = adapter->sessionId;
cap_tsf_params.tsf_rsp_cb_func = hdd_tsf_cb;
cap_tsf_params.tsf_rsp_cb_ctx = adapter;
ret = sme_get_tsf_req(hdd_ctx->hHal, cap_tsf_params);
if (ret != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("capture fail"));
buf[0] = TSF_CAPTURE_FAIL;
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
return -EINVAL;
}
/* wait till we get a response from fw */
status = vos_wait_single_event(&adapter->tsf_cap_ctx.
tsf_capture_done_event,
HDD_TSF_GET_REQ_TIMEOUT);
if (!VOS_IS_STATUS_SUCCESS(status)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
("capture tsf vos wait for single_event failed!! %d"),
status);
vos_spin_lock_acquire(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_spin_lock_release(&adapter->tsf_cap_ctx.tsf_lock);
return status;
}
buf[1] = adapter->tsf_cap_ctx.tsf_low;
buf[2] = adapter->tsf_cap_ctx.tsf_high;
hddLog(VOS_TRACE_LEVEL_INFO,
FL("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u"),
buf[0], buf[1], buf[2]);
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ioctl return cap tsf cmd, ret = %d"), ret);
return ret;
}
void wlan_hdd_tsf_init(hdd_adapter_t *adapter)
{
if (adapter == NULL) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("TSF init on a null adapter!"));
return;
}
adapter->tsf_cap_ctx.tsf_get_state = TSF_RETURN;
adapter->tsf_cap_ctx.tsf_capture_state = TSF_IDLE;
vos_event_init(&adapter->tsf_cap_ctx.tsf_capture_done_event);
vos_spin_lock_init(&adapter->tsf_cap_ctx.tsf_lock);
adapter->tsf_cap_ctx.tsf_high = 0;
adapter->tsf_cap_ctx.tsf_low = 0;
}
#endif
bool hdd_is_cli_iface_up(hdd_context_t *hdd_ctx)
{
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
hdd_adapter_t *adapter;
VOS_STATUS status;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if ((adapter->device_mode == WLAN_HDD_INFRA_STATION ||
adapter->device_mode == WLAN_HDD_P2P_CLIENT) &&
test_bit(DEVICE_IFACE_OPENED,
&adapter->event_flags)){
return true;
}
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
return false;
}
//Register the module init/exit functions
module_init(hdd_module_init);
module_exit(hdd_module_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Qualcomm Atheros, Inc.");
MODULE_DESCRIPTION("WLAN HOST DEVICE DRIVER");
static const struct kernel_param_ops con_mode_ops = {
.set = con_mode_handler,
.get = param_get_int,
};
static const struct kernel_param_ops fwpath_ops = {
.set = fwpath_changed_handler,
.get = param_get_string,
};
#ifdef MODULE
module_param(con_mode, int, 0);
#else
module_param_cb(con_mode, &con_mode_ops, &con_mode,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
module_param_cb(fwpath, &fwpath_ops, &fwpath,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
module_param(enable_dfs_chan_scan, int,
S_IRUSR | S_IRGRP | S_IROTH);
module_param(enable_11d, int,
S_IRUSR | S_IRGRP | S_IROTH);
module_param(country_code, charp,
S_IRUSR | S_IRGRP | S_IROTH);