blob: e77f387f258bca7bca51783fcacdef78c76b3875 [file] [log] [blame]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/*========================================================================
\file wlan_hdd_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<net/addrconf.h>
#include <wlan_hdd_includes.h>
#include <vos_api.h>
#include <vos_sched.h>
#ifdef WLAN_FEATURE_LPSS
#include <vos_utils.h>
#endif
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include <wcnss_api.h>
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <dbglog_host.h>
#include <wlan_logging_sock_svc.h>
#include <wlan_hdd_wowl.h>
#include <wlan_hdd_misc.h>
#include <wlan_hdd_wext.h>
#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>
#include "sapApi.h"
#include <linux/semaphore.h>
#include <linux/ctype.h>
#include <linux/compat.h>
#include <linux/pm_qos.h>
#ifdef MSM_PLATFORM
#ifdef CONFIG_CNSS
#include <soc/qcom/subsystem_restart.h>
#endif
#endif
#include <wlan_hdd_hostapd.h>
#include <wlan_hdd_softap_tx_rx.h>
#include "cfgApi.h"
#include "wlan_hdd_dev_pwr.h"
#include "qwlan_version.h"
#include "wlan_qct_wda.h"
#include "wlan_hdd_tdls.h"
#ifdef FEATURE_WLAN_CH_AVOID
#include "vos_cnss.h"
#include "regdomain_common.h"
extern int hdd_hostapd_stop (struct net_device *dev);
#endif /* FEATURE_WLAN_CH_AVOID */
#ifdef WLAN_FEATURE_NAN
#include "wlan_hdd_nan.h"
#endif /* WLAN_FEATURE_NAN */
#include "wlan_hdd_debugfs.h"
#include "epping_main.h"
#include "wlan_hdd_memdump.h"
#include <wlan_hdd_ipa.h>
#if defined(HIF_PCI)
#include "if_pci.h"
#elif defined(HIF_USB)
#include "if_usb.h"
#elif defined(HIF_SDIO)
#include "if_ath_sdio.h"
#endif
#include "wma.h"
#include "ol_fw.h"
#include "wlan_hdd_ocb.h"
#include "wlan_hdd_tsf.h"
#include "tl_shim.h"
#include "wlan_hdd_oemdata.h"
#include "sirApi.h"
#ifdef CNSS_GENL
#include <net/cnss_nl.h>
#endif
#if defined(LINUX_QCMBR)
#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
#endif
#ifdef QCA_ARP_SPOOFING_WAR
#include "ol_if_athvar.h"
#define HDD_ARP_PACKET_TYPE_OFFSET 12
#endif
#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
#ifdef IPA_UC_OFFLOAD
/* If IPA UC data path is enabled, target should reserve extra tx descriptors
* for IPA WDI data path.
* Then host data path should allow less TX packet pumping in case
* IPA WDI data path enabled */
#define WLAN_TFC_IPAUC_TX_DESC_RESERVE 100
#endif /* IPA_UC_OFFLOAD */
/* 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;
static char fwpath_mode_local[BUF_LEN];
#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 */
/*
* 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)
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
#define TID_MIN_VALUE 0
#define TID_MAX_VALUE 15
#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
/*
* Driver miracast parameters 0-Disabled
* 1-Source, 2-Sink
*/
#define WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL 0
#define WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL 2
/*
* When ever we need to print IBSSPEERINFOALL for more than 16 STA
* we will split the printing.
*/
#define NUM_OF_STA_DATA_TO_PRINT 16
#define WLAN_NLINK_CESIUM 30
/*Nss - 1, (Nss = 2 for 2x2)*/
#define NUM_OF_SOUNDING_DIMENSIONS 1
/*
* Android DRIVER command structures
*/
struct android_wifi_reassoc_params {
unsigned char bssid[18];
int channel;
};
#define ANDROID_WIFI_ACTION_FRAME_SIZE 1040
struct android_wifi_af_params {
unsigned char bssid[18];
int channel;
int dwell_time;
int len;
unsigned char data[ANDROID_WIFI_ACTION_FRAME_SIZE];
} ;
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
#define WLAN_HDD_MAX_TCP_PORT 65535
#define WLAN_WAIT_TIME_READY_TO_EXTWOW 2000
#endif
#define AUTO_SUSPEND_DELAY_MS 1500
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;
#define WOW_MAX_FILTER_LISTS 1
#define WOW_MAX_FILTERS_PER_LIST 4
#define WOW_MIN_PATTERN_SIZE 6
#define WOW_MAX_PATTERN_SIZE 64
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS)
static const struct wiphy_wowlan_support wowlan_support_reg_init = {
.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE |
WIPHY_WOWLAN_EAP_IDENTITY_REQ |
WIPHY_WOWLAN_4WAY_HANDSHAKE |
WIPHY_WOWLAN_RFKILL_RELEASE,
.n_patterns = WOW_MAX_FILTER_LISTS * WOW_MAX_FILTERS_PER_LIST,
.pattern_min_len = WOW_MIN_PATTERN_SIZE,
.pattern_max_len = WOW_MAX_PATTERN_SIZE,
};
#endif
/* Internal function declarations */
static int hdd_driver_init(void);
static void hdd_driver_exit(void);
/* Internal function declarations */
static void hdd_tx_fail_ind_callback(v_U8_t *MacAddr, v_U8_t seqNo);
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);
void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback);
/**
* struct init_comp - Driver loading status
* @wlan_start_comp: Completion event
* @status: Success/Failure
*/
struct init_comp {
struct completion wlan_start_comp;
int status;
};
static struct init_comp wlan_comp;
/* Keep track unload status */
static uint32_t g_current_unload_state;
#define TRACK_UNLOAD_STATUS(state) (g_current_unload_state = state)
#ifdef QCA_WIFI_FTM
extern int hdd_ftm_stop(hdd_context_t *pHddCtx);
#endif
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
v_VOID_t wlan_hdd_auto_shutdown_cb(v_VOID_t);
#endif
/* Store WLAN driver version 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_version[ ] = QWLAN_VERSIONSTR;
/**
* hdd_device_mode_to_string() - return string conversion of device mode
* @device_mode: device mode
*
* This utility function helps log string conversion of device mode.
*
* Return: string conversion of device mode, if match found;
* "Unknown" otherwise.
*/
const char* hdd_device_mode_to_string(uint8_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);
CASE_RETURN_STRING(WLAN_HDD_OCB);
CASE_RETURN_STRING(WLAN_HDD_NDI);
default:
return "Unknown";
}
}
#ifdef QCA_LL_TX_FLOW_CT
/**
* wlan_hdd_clean_tx_flow_control_timer - Function cleans tx flow control timer
* @hddctx: pointer to hddctx
* @hdd_adapter_t: pointer to hdd_adapter_t
*
* Function deregister's, destroy tx flow control timer
*
* Return: None
*/
void wlan_hdd_clean_tx_flow_control_timer(hdd_context_t *hddctx,
hdd_adapter_t *adapter)
{
WLANTL_DeRegisterTXFlowControl(hddctx->pvosContext,
adapter->sessionId);
if (adapter->tx_flow_timer_initialized == VOS_TRUE) {
vos_timer_destroy(&adapter->tx_flow_control_timer);
adapter->tx_flow_timer_initialized = VOS_FALSE;
}
}
#endif
/**
* wlan_hdd_find_opclass() - Find operating class for a channel
* @hal: handler to HAL
* @channel: channel id
* @bw_offset: bandwidth offset
*
* Function invokes sme api to find the operating class
*
* Return: operating class
*/
uint8_t wlan_hdd_find_opclass(tHalHandle hal, uint8_t channel,
uint8_t bw_offset)
{
uint8_t opclass = 0;
sme_get_opclass(hal, channel, bw_offset, &opclass);
return opclass;
}
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
void hdd_csa_notify_cb
(
void *hdd_context,
void *indi_param
);
#endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
#ifdef FEATURE_GREEN_AP
static void hdd_wlan_green_ap_timer_fn(void *phddctx)
{
hdd_context_t *pHddCtx = (hdd_context_t *)phddctx;
hdd_green_ap_ctx_t *green_ap;
if (0 != wlan_hdd_validate_context(pHddCtx))
return;
green_ap = pHddCtx->green_ap_ctx;
if (green_ap)
hdd_wlan_green_ap_mc(pHddCtx, green_ap->ps_event);
}
static VOS_STATUS hdd_wlan_green_ap_attach(hdd_context_t *pHddCtx)
{
hdd_green_ap_ctx_t *green_ap;
VOS_STATUS status = VOS_STATUS_SUCCESS;
ENTER();
green_ap = vos_mem_malloc(sizeof(hdd_green_ap_ctx_t));
if (!green_ap) {
hddLog(LOGP, FL("Memory allocation for Green-AP failed!"));
status = VOS_STATUS_E_NOMEM;
goto error;
}
vos_mem_zero((void *)green_ap, sizeof(*green_ap));
green_ap->pHddContext = pHddCtx;
pHddCtx->green_ap_ctx = green_ap;
green_ap->ps_state = GREEN_AP_PS_OFF_STATE;
green_ap->ps_event = 0;
green_ap->num_nodes = 0;
green_ap->ps_on_time = GREEN_AP_PS_ON_TIME;
green_ap->ps_delay_time = GREEN_AP_PS_DELAY_TIME;
vos_timer_init(&green_ap->ps_timer,
VOS_TIMER_TYPE_SW,
hdd_wlan_green_ap_timer_fn,
(void *)pHddCtx);
error:
EXIT();
return status;
}
static VOS_STATUS hdd_wlan_green_ap_deattach(hdd_context_t *pHddCtx)
{
hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx;
VOS_STATUS status = VOS_STATUS_SUCCESS;
ENTER();
if (green_ap == NULL) {
hddLog(LOG1, FL("Green-AP is not enabled"));
status = VOS_STATUS_E_NOSUPPORT;
goto done;
}
/* check if the timer status is destroyed */
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&green_ap->ps_timer))
{
vos_timer_stop(&green_ap->ps_timer);
}
/* Destroy the Green AP timer */
if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&green_ap->ps_timer)))
{
hddLog(LOG1, FL("Cannot deallocate Green-AP's timer"));
}
/* release memory */
vos_mem_zero((void *)green_ap, sizeof(*green_ap));
vos_mem_free(green_ap);
pHddCtx->green_ap_ctx = NULL;
done:
EXIT();
return status;
}
static void hdd_wlan_green_ap_update(hdd_context_t *pHddCtx,
hdd_green_ap_ps_state_t state,
hdd_green_ap_event_t event)
{
hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx;
green_ap->ps_state = state;
green_ap->ps_event = event;
}
static int hdd_wlan_green_ap_enable(hdd_adapter_t *pHostapdAdapter,
v_U8_t enable)
{
int ret = 0;
hddLog(LOG1, "%s: Set Green-AP val: %d", __func__, enable);
ret = process_wma_set_command(
(int)pHostapdAdapter->sessionId,
(int)WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID,
enable,
DBG_CMD);
return ret;
}
void hdd_wlan_green_ap_mc(hdd_context_t *pHddCtx,
hdd_green_ap_event_t event)
{
hdd_green_ap_ctx_t *green_ap = pHddCtx->green_ap_ctx;
hdd_adapter_t *pAdapter = NULL;
if (green_ap == NULL)
return ;
hddLog(LOG1, "%s: Green-AP event: %d, state: %d, num_nodes: %d",
__func__, event, green_ap->ps_state, green_ap->num_nodes);
/* handle the green ap ps event */
switch(event) {
case GREEN_AP_PS_START_EVENT:
green_ap->ps_enable = 1;
break;
case GREEN_AP_PS_STOP_EVENT:
green_ap->ps_enable = 0;
break;
case GREEN_AP_ADD_STA_EVENT:
green_ap->num_nodes++;
break;
case GREEN_AP_DEL_STA_EVENT:
if (green_ap->num_nodes)
green_ap->num_nodes--;
break;
case GREEN_AP_PS_ON_EVENT:
case GREEN_AP_PS_WAIT_EVENT:
break;
default:
hddLog(LOGE, "%s: invalid event %d", __func__, event);
break;
}
pAdapter = hdd_get_adapter (pHddCtx, WLAN_HDD_SOFTAP );
if (pAdapter == NULL) {
hddLog(LOGE, FL("Green-AP no SAP adapter"));
goto done;
}
/* Confirm that power save is enabled before doing state transitions */
if (!green_ap->ps_enable) {
hddLog(VOS_TRACE_LEVEL_INFO, FL("green ap is disabled"));
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT);
if (hdd_wlan_green_ap_enable(pAdapter, 0))
hddLog(LOGE, FL("failed to set green ap mode"));
goto done;
}
/* handle the green ap ps state */
switch(green_ap->ps_state) {
case GREEN_AP_PS_IDLE_STATE:
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT);
break;
case GREEN_AP_PS_OFF_STATE:
if (!green_ap->num_nodes) {
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_WAIT_STATE, GREEN_AP_PS_WAIT_EVENT);
vos_timer_start(&green_ap->ps_timer,
green_ap->ps_delay_time);
}
break;
case GREEN_AP_PS_WAIT_STATE:
if (!green_ap->num_nodes) {
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_ON_STATE, GREEN_AP_PS_WAIT_EVENT);
hdd_wlan_green_ap_enable(pAdapter, 1);
if (green_ap->ps_on_time) {
hdd_wlan_green_ap_update(pHddCtx,
0, GREEN_AP_PS_WAIT_EVENT);
vos_timer_start(&green_ap->ps_timer,
green_ap->ps_on_time);
}
} else {
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT);
}
break;
case GREEN_AP_PS_ON_STATE:
if (green_ap->num_nodes) {
if (hdd_wlan_green_ap_enable(pAdapter, 0)) {
hddLog(LOGE, FL("FAILED TO SET GREEN-AP mode"));
goto done;
}
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT);
} else if ((green_ap->ps_event == GREEN_AP_PS_WAIT_EVENT) &&
(green_ap->ps_on_time)) {
/* ps_on_time timeout, switch to ps off */
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_WAIT_STATE, GREEN_AP_PS_ON_EVENT);
if (hdd_wlan_green_ap_enable(pAdapter, 0)) {
hddLog(LOGE, FL("FAILED TO SET GREEN-AP mode"));
goto done;
}
vos_timer_start(&green_ap->ps_timer,
green_ap->ps_delay_time);
}
break;
default:
hddLog(LOGE, "%s: invalid state %d", __func__, green_ap->ps_state);
hdd_wlan_green_ap_update(pHddCtx,
GREEN_AP_PS_OFF_STATE, GREEN_AP_PS_WAIT_EVENT);
break;
}
done:
return;
}
/**
* hdd_wlan_green_ap_init() - Initialize Green AP feature
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_init(struct hdd_context_s *hdd_ctx)
{
if (!VOS_IS_STATUS_SUCCESS(hdd_wlan_green_ap_attach(hdd_ctx)))
hddLog(LOGE, FL("Failed to allocate Green-AP resource"));
}
/**
* hdd_wlan_green_ap_deinit() - De-initialize Green AP feature
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_deinit(struct hdd_context_s *hdd_ctx)
{
if (!VOS_IS_STATUS_SUCCESS(hdd_wlan_green_ap_deattach(hdd_ctx)))
hddLog(LOGE, FL("Cannot deallocate Green-AP resource"));
}
/**
* wlan_hdd_set_egap_support() - helper function to set egap support flag
* @hdd_ctx: pointer to hdd context
* @cfg: pointer to hdd target configuration knob
*
* Return: None
*/
void wlan_hdd_set_egap_support(hdd_context_t *hdd_ctx, struct hdd_tgt_cfg *cfg)
{
if (hdd_ctx && cfg)
hdd_ctx->green_ap_ctx->egap_support = cfg->egap_support;
}
/**
* hdd_wlan_is_egap_enabled() - Get Enhance Green AP feature status
* @fw_egap_support: flag whether firmware supports egap or not
* @cfg: pointer to the struct hdd_config_t
*
* Return: true if firmware, feature_flag and ini are all enabled the egap
*/
static bool hdd_wlan_is_egap_enabled(bool fw_egap_support, hdd_config_t *cfg)
{
/* check if the firmware and ini are both enabled the egap,
* and also the feature_flag enable.
*/
if (fw_egap_support && cfg->enable_egap &&
cfg->egap_feature_flag)
return true;
return false;
}
/**
* hdd_wlan_enable_egap() - Enable Enhance Green AP
* @hdd_ctx: HDD global context
*
* Return: 0 on success, negative errno on failure
*/
int hdd_wlan_enable_egap(struct hdd_context_s *hdd_ctx)
{
hdd_config_t *cfg;
if (!hdd_ctx) {
hddLog(LOGE, FL("hdd context is NULL"));
return -EINVAL;
}
cfg = hdd_ctx->cfg_ini;
if (!cfg) {
hddLog(LOGE, FL("hdd cfg is NULL"));
return -EINVAL;
}
if (!hdd_ctx->green_ap_ctx) {
hddLog(LOGE, FL("green ap context is NULL"));
return -EINVAL;
}
if (!hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support,
hdd_ctx->cfg_ini))
return -ENOTSUPP;
if (VOS_STATUS_SUCCESS != sme_send_egap_conf_params(cfg->enable_egap,
cfg->egap_inact_time,
cfg->egap_wait_time,
cfg->egap_feature_flag))
return -EINVAL;
return 0;
}
/**
* hdd_wlan_green_ap_start_bss() - Notify Green AP of Start BSS event
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_start_bss(struct hdd_context_s *hdd_ctx)
{
hdd_config_t *cfg;
if (!hdd_ctx) {
hddLog(LOGE, FL("hdd context is NULL"));
return;
}
cfg = hdd_ctx->cfg_ini;
if (!cfg) {
hddLog(LOGE, FL("hdd cfg is NULL"));
return;
}
if (!hdd_ctx->green_ap_ctx) {
hddLog(LOGE, FL("green ap context is NULL"));
return;
}
if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support,
hdd_ctx->cfg_ini))
return;
if ((hdd_ctx->concurrency_mode & VOS_SAP) &&
!(hdd_ctx->concurrency_mode & (~VOS_SAP)) &&
cfg->enable2x2 && cfg->enableGreenAP) {
hddLog(LOG1,
FL("Green AP enabled - sta_con: %d, 2x2: %d, GAP: %d"),
(VOS_STA & hdd_ctx->concurrency_mode),
cfg->enable2x2, cfg->enableGreenAP);
hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_START_EVENT);
} else {
hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_STOP_EVENT);
hddLog(LOG1,
FL("Green AP disabled- sta_con: %d, 2x2: %d, GAP: %d"),
(VOS_STA & hdd_ctx->concurrency_mode),
cfg->enable2x2, cfg->enableGreenAP);
}
}
/**
* hdd_wlan_green_ap_stop_bss() - Notify Green AP of Stop BSS event
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_stop_bss(struct hdd_context_s *hdd_ctx)
{
if (!hdd_ctx) {
hddLog(LOGE, FL("hdd context is NULL"));
return;
}
if (!hdd_ctx->cfg_ini) {
hddLog(LOGE, FL("hdd cfg is NULL"));
return;
}
if (!hdd_ctx->green_ap_ctx) {
hddLog(LOGE, FL("green ap context is NULL"));
return;
}
if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support,
hdd_ctx->cfg_ini))
return;
/* For AP+AP mode, only trigger GREEN_AP_PS_STOP_EVENT, when the
* last AP stops.
*/
if (1 == (hdd_ctx->no_of_open_sessions[VOS_STA_SAP_MODE]))
hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_PS_STOP_EVENT);
}
/**
* hdd_wlan_green_ap_add_sta() - Notify Green AP of Add Station event
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_add_sta(struct hdd_context_s *hdd_ctx)
{
if (!hdd_ctx) {
hddLog(LOGE, FL("hdd context is NULL"));
return;
}
if (!hdd_ctx->cfg_ini) {
hddLog(LOGE, FL("hdd cfg is NULL"));
return;
}
if (!hdd_ctx->green_ap_ctx) {
hddLog(LOGE, FL("green ap context is NULL"));
return;
}
if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support,
hdd_ctx->cfg_ini))
return;
hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_ADD_STA_EVENT);
}
/**
* hdd_wlan_green_ap_del_sta() - Notify Green AP of Delete Station event
* @hdd_ctx: HDD global context
*
* Return: none
*/
void hdd_wlan_green_ap_del_sta(struct hdd_context_s *hdd_ctx)
{
if (!hdd_ctx) {
hddLog(LOGE, FL("hdd context is NULL"));
return;
}
if (!hdd_ctx->cfg_ini) {
hddLog(LOGE, FL("hdd cfg is NULL"));
return;
}
if (!hdd_ctx->green_ap_ctx) {
hddLog(LOGE, FL("green ap context is NULL"));
return;
}
if (hdd_wlan_is_egap_enabled(hdd_ctx->green_ap_ctx->egap_support,
hdd_ctx->cfg_ini))
return;
hdd_wlan_green_ap_mc(hdd_ctx, GREEN_AP_DEL_STA_EVENT);
}
#endif /* FEATURE_GREEN_AP */
/**
* hdd_lost_link_info_cb() - callback function to get lost link information
* @context: HDD context
* @lost_link_info: lost link information
*
* Return: none
*/
static void hdd_lost_link_info_cb(void *context,
struct sir_lost_link_info *lost_link_info)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)context;
int status;
hdd_adapter_t *adapter;
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
if (NULL == lost_link_info) {
hddLog(LOGE, "%s: lost_link_info is NULL", __func__);
return;
}
adapter = hdd_get_adapter_by_vdev(hdd_ctx, lost_link_info->vdev_id);
if (NULL == adapter) {
hddLog(LOGE, "%s: invalid adapter", __func__);
return;
}
adapter->rssi_on_disconnect = lost_link_info->rssi;
hddLog(LOG1, "%s: rssi on disconnect %d",
__func__, adapter->rssi_on_disconnect);
}
/**
* __hdd_smps_force_mode_cb() - callback for smps force mode
* event
* @context: HDD context
* @smps_mode_event: smps force mode event info
*
* Return: none
*/
static void __hdd_smps_force_mode_cb(void *context,
struct sir_smps_force_mode_event *smps_mode_event)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)context;
int status;
hdd_adapter_t *adapter;
ENTER();
status = wlan_hdd_validate_context(hdd_ctx);
if (0 != status)
return;
if (NULL == smps_mode_event) {
hddLog(LOGE, FL("smps_mode_event is NULL"));
return;
}
adapter = hdd_get_adapter_by_vdev(hdd_ctx, smps_mode_event->vdev_id);
if (NULL == adapter) {
hddLog(LOGE, FL("Invalid adapter"));
return;
}
adapter->smps_force_mode_status = smps_mode_event->status;
complete(&adapter->smps_force_mode_comp_var);
hddLog(LOG1, FL("status %d vdev_id: %d"),
smps_mode_event->status, smps_mode_event->vdev_id);
}
/**
* hdd_smps_force_mode_cb() - Wrapper to protect
* __hdd_smps_force_mode_cb callback for smps force mode event
* @context: HDD context
* @smps_mode_event: smps force mode event info
*
* Return: none
*/
static void hdd_smps_force_mode_cb(void *context,
struct sir_smps_force_mode_event *smps_mode_event)
{
vos_ssr_protect(__func__);
__hdd_smps_force_mode_cb(context, smps_mode_event);
vos_ssr_unprotect(__func__);
}
#if defined (FEATURE_WLAN_MCC_TO_SCC_SWITCH) || defined (FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE) || defined (FEATURE_WLAN_CH_AVOID)
/**
* 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
*/
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 CFG80211_DEL_STA_V2
struct station_del_parameters delStaParams;
#endif
tsap_Config_t *pConfig;
void *p_sap_ctx;
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
pConfig = &pHddApCtx->sapConfig;
p_sap_ctx = pHddApCtx->sapContext;
mutex_lock(&pHddCtx->sap_lock);
if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) {
#ifdef 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->stop_bss_event);
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(p_sap_ctx)) {
vos_status = vos_wait_single_event(&pHostapdState->stop_bss_event,
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(LOGE, FL("SAP Stop Success"));
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
/*this delay is needed to ensure proper resource cleanup of SAP*/
vos_sleep(1000);
#endif //#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
if (pHddCtx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss);
if (0 != wlan_hdd_cfg80211_update_apies(ap_adapter)) {
hddLog(LOGE, FL("SAP Not able to set AP IEs"));
WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL);
goto end;
}
vos_event_reset(&pHostapdState->vosEvent);
if (WLANSAP_StartBss(p_sap_ctx, hdd_hostapd_SAPEventCB, pConfig,
(v_PVOID_t)ap_adapter->dev) != VOS_STATUS_SUCCESS) {
hddLog(LOGE, FL("SAP Start Bss fail"));
WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL);
goto end;
}
hddLog(LOG1, FL("Waiting for SAP to start"));
vos_status = vos_wait_single_event(&pHostapdState->vosEvent, 10000);
WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
hddLog(LOGE, FL("SAP Start failed"));
goto end;
}
hddLog(LOGE, 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 (pHddCtx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.obss);
}
end:
mutex_unlock(&pHddCtx->sap_lock);
return;
}
#endif
static int __hdd_netdev_notifier_call(struct notifier_block * nb,
unsigned long state,
void *data)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0))
struct netdev_notifier_info *dev_notif_info = data;
struct net_device *dev = dev_notif_info->dev;
#else
struct net_device *dev = data;
#endif
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
//Make sure that this callback corresponds to our device.
if ((strncmp(dev->name, "wlan", 4)) &&
(strncmp(dev->name, "softAP", 6)) &&
(strncmp(dev->name, "p2p", 3)))
return NOTIFY_DONE;
if ((pAdapter->magic != WLAN_HDD_ADAPTER_MAGIC) ||
(pAdapter->dev != dev)) {
hddLog(LOGE, FL("device adapter is not matching!!!"));
return NOTIFY_DONE;
}
if (!dev->ieee80211_ptr) {
hddLog(LOGE, FL("ieee80211_ptr is NULL!!!"));
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:
#ifdef FEATURE_WLAN_CH_AVOID
sme_ChAvoidUpdateReq(pHddCtx->hHal);
#endif
break;
case NETDEV_DOWN:
break;
case NETDEV_CHANGE:
if(TRUE == pAdapter->isLinkUpSvcNeeded)
complete(&pAdapter->linkup_event_var);
break;
case NETDEV_GOING_DOWN:
if( pAdapter->scan_info.mScanPending != FALSE )
{
unsigned long rc;
INIT_COMPLETION(pAdapter->scan_info.abortscan_event_var);
hdd_abort_mac_scan(pAdapter->pHddCtx, pAdapter->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
rc = wait_for_completion_timeout(
&pAdapter->scan_info.abortscan_event_var,
msecs_to_jiffies(WLAN_WAIT_TIME_ABORTSCAN));
if (!rc) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Timeout occurred while waiting for abortscan",
__func__);
}
}
else
{
vos_flush_work(&pAdapter->scan_block_work);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Scan is not Pending from user" , __func__);
}
break;
default:
break;
}
return NOTIFY_DONE;
}
/**
* hdd_netdev_notifier_call() - netdev notifier callback function
* @nb: pointer to notifier block
* @state: state
* @ndev: ndev pointer
*
* Return: 0 on success, error number otherwise.
*/
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 user space 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 user space. 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
/**---------------------------------------------------------------------------
\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)
{
VOS_TRACE_LEVEL 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;
}
}
/*
* 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) {
hddLog(LOG1, FL("%pS HDD context is Null"), (void *)_RET_IP_);
return -ENODEV;
}
if (pHddCtx->isLogpInProgress) {
hddLog(LOG1, FL("%pS LOGP in Progress. Ignore!!!"), (void *)_RET_IP_);
return -EAGAIN;
}
if ((pHddCtx->isLoadInProgress) ||
(pHddCtx->isUnloadInProgress)) {
hddLog(LOG1,
FL("%pS loading: %d unloading:%d in Progress. Ignore!!!"),
(void *)_RET_IP_,
pHddCtx->isLoadInProgress,
pHddCtx->isUnloadInProgress);
if (pHddCtx->isUnloadInProgress)
hddLog(LOG1, "current unload state: %d", g_current_unload_state);
return -EAGAIN;
}
return 0;
}
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);
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)
{
unsigned long rc;
rc = wait_for_completion_timeout(&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!rc)
hddLog(LOGE, FL("failure waiting for disconnect_comp_var"));
}
}
}
static 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))
{
/*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);
}
}
/* Function header is left blank intentionally */
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);
if (NULL == inPtr)
{
return 0;
}
else if (SPACE_ASCII_VALUE != *inPtr)
{
return 0;
}
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
if ('\0' == *inPtr)
{
return 0;
}
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 is 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);
if (NULL == inPtr)
{
return -EINVAL;
}
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
if ('\0' == *inPtr)
{
return 0;
}
sscanf(inPtr, "%31s ", buf);
v = kstrtos32(buf, 10, &tempInt);
if ( v < 0)
{
return -EINVAL;
}
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;
}
/* Function header is 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);
if (NULL == inPtr)
{
return -EINVAL;
}
else if (SPACE_ASCII_VALUE != *inPtr)
{
return -EINVAL;
}
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr)) inPtr++;
if ('\0' == *inPtr)
{
return 0;
}
sscanf(inPtr, "%31s ", buf);
v = kstrtos32(buf, 10, &tempInt);
if ( v < 0)
{
return -EINVAL;
}
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;
break;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rate: %d", *pRate);
return 0;
}
/**
* hdd_get_ibss_peer_info_cb() - IBSS peer Info request callback
* @UserData: Adapter private data
* @pPeerInfoRsp: Peer info response
*
* This is an asynchronous callback function from SME when the peer info
* is received
*
* Return: 0 for success non-zero for failure
*/
void
hdd_get_ibss_peer_info_cb(v_VOID_t *pUserData,
tSirPeerInfoRspParams *pPeerInfo)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *)pUserData;
hdd_station_ctx_t *pStaCtx;
v_U8_t i;
/*Sanity check*/
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"invalid adapter or adapter has invalid magic");
return;
}
pStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (NULL != pPeerInfo && eHAL_STATUS_SUCCESS == pPeerInfo->status) {
/* validate number of peers */
if (pPeerInfo->numPeers > HDD_MAX_NUM_IBSS_STA) {
hddLog(LOGW,
FL("Limiting num_peers %u to %u"),
pPeerInfo->numPeers, HDD_MAX_NUM_IBSS_STA);
pPeerInfo->numPeers = HDD_MAX_NUM_IBSS_STA;
}
pStaCtx->ibss_peer_info.status = pPeerInfo->status;
pStaCtx->ibss_peer_info.numPeers = pPeerInfo->numPeers;
for (i = 0; i < pPeerInfo->numPeers; i++) {
pStaCtx->ibss_peer_info.peerInfoParams[i] =
pPeerInfo->peerInfoParams[i];
}
} else {
hddLog(LOGE, FL("peerInfo %s: status %u, numPeers %u"),
pPeerInfo ? "valid" : "null",
pPeerInfo ? pPeerInfo->status : eHAL_STATUS_FAILURE,
pPeerInfo ? pPeerInfo->numPeers : 0);
pStaCtx->ibss_peer_info.numPeers = 0;
pStaCtx->ibss_peer_info.status = eHAL_STATUS_FAILURE;
}
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);
VOS_STATUS retStatus = VOS_STATUS_E_FAILURE;
unsigned long rc;
INIT_COMPLETION(pAdapter->ibss_peer_info_comp);
retStatus = sme_RequestIBSSPeerInfo(hHal, pAdapter,
hdd_get_ibss_peer_info_cb,
VOS_TRUE, 0xFF);
if (VOS_STATUS_SUCCESS == retStatus)
{
rc = wait_for_completion_timeout
(&pAdapter->ibss_peer_info_comp,
msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
/* status will be 0 if timed out */
if (!rc) {
hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT",
__func__);
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)
{
unsigned long rc;
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_get_ibss_peer_info_cb,
VOS_FALSE, staIdx);
if (VOS_STATUS_SUCCESS == retStatus)
{
rc = wait_for_completion_timeout
(&pAdapter->ibss_peer_info_comp,
msecs_to_jiffies(IBSS_PEER_INFO_REQ_TIMOEUT));
/* status = 0 on timeout */
if (!rc) {
hddLog(VOS_TRACE_LEVEL_WARN, "%s: Warning: IBSS_PEER_INFO_TIMEOUT",
__func__);
retStatus = VOS_STATUS_E_FAILURE;
return retStatus;
}
}
else
{
hddLog(VOS_TRACE_LEVEL_WARN,
"%s: Warning: sme_RequestIBSSPeerInfo Request failed", __func__);
}
return retStatus;
}
/* Function header is left blank intentionally */
static VOS_STATUS
hdd_parse_get_ibss_peer_info(tANI_U8 *pValue, v_MACADDR_t *pPeerMacAddr)
{
tANI_U8 *inPtr = pValue;
size_t inPtrLen = strlen(pValue);
inPtr = strnchr(pValue, inPtrLen, SPACE_ASCII_VALUE);
if (NULL == inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
else if (SPACE_ASCII_VALUE != *inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
while ((SPACE_ASCII_VALUE == *inPtr) && ('\0' != *inPtr) ) inPtr++;
if ('\0' == *inPtr)
{
return VOS_STATUS_E_FAILURE;;
}
inPtrLen -= (inPtr - pValue);
if (inPtrLen < 17)
{
return VOS_STATUS_E_FAILURE;
}
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]);
return VOS_STATUS_SUCCESS;
}
#ifdef IPA_UC_STA_OFFLOAD
static void hdd_set_thermal_level_cb(hdd_context_t *pHddCtx, u_int8_t level)
{
/* Change IPA to SW path when throttle level greater than 0 */
if (level > THROTTLE_LEVEL_0)
hdd_ipa_send_mcc_scc_msg(pHddCtx, TRUE);
else
/* restore original concurrency mode */
hdd_ipa_send_mcc_scc_msg(pHddCtx, pHddCtx->mcc_mode);
}
#else
static void hdd_set_thermal_level_cb(hdd_context_t *pHddCtx, u_int8_t level)
{
}
#endif
#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
static bool
hdd_system_suspend_state(hdd_context_t *hdd_ctx)
{
unsigned long flags;
bool s;
spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
s = hdd_ctx->system_suspended;
spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
return s;
}
bool
hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
{
unsigned long flags;
bool old;
spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
old = hdd_ctx->system_suspended;
hdd_ctx->system_suspended = state;
spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
return old;
}
int
hdd_thermal_suspend_state(hdd_context_t *hdd_ctx)
{
unsigned long flags;
int s;
spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
s = hdd_ctx->thermal_suspend_state;
spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
return s;
}
static bool
hdd_thermal_suspend_transit(hdd_context_t *hdd_ctx, int target, int *old)
{
unsigned long flags;
int s;
bool ret = false;
spin_lock_irqsave(&hdd_ctx->thermal_suspend_lock, flags);
s = hdd_ctx->thermal_suspend_state;
if (old)
*old = s;
switch (target) {
case HDD_WLAN_THERMAL_ACTIVE:
if (s == HDD_WLAN_THERMAL_RESUMING ||
s == HDD_WLAN_THERMAL_SUSPENDING)
ret = true;
break;
case HDD_WLAN_THERMAL_SUSPENDING:
if (s == HDD_WLAN_THERMAL_ACTIVE)
ret = true;
break;
case HDD_WLAN_THERMAL_SUSPENDED:
if (s == HDD_WLAN_THERMAL_SUSPENDING ||
s == HDD_WLAN_THERMAL_RESUMING)
ret = true;
break;
case HDD_WLAN_THERMAL_RESUMING:
if (s == HDD_WLAN_THERMAL_SUSPENDED)
ret = true;
break;
case HDD_WLAN_THERMAL_DEINIT:
if (s != HDD_WLAN_THERMAL_DEINIT)
ret = true;
break;
}
if (ret)
hdd_ctx->thermal_suspend_state = target;
spin_unlock_irqrestore(&hdd_ctx->thermal_suspend_lock, flags);
return ret;
}
static void
hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx)
{
int state;
bool okay;
if (!hdd_ctx->thermal_suspend_wq)
return;
cancel_delayed_work_sync(&hdd_ctx->thermal_suspend_work);
okay = hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_DEINIT,
&state);
destroy_workqueue(hdd_ctx->thermal_suspend_wq);
hdd_ctx->thermal_suspend_wq = NULL;
if (!okay || state == HDD_WLAN_THERMAL_ACTIVE)
return;
if (state == HDD_WLAN_THERMAL_SUSPENDED) {
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
__wlan_hdd_cfg80211_resume_wlan(hdd_ctx->wiphy, true);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
}
}
static void
hdd_thermal_suspend_work(struct work_struct *work)
{
hdd_context_t *hdd_ctx =
container_of(work, hdd_context_t, thermal_suspend_work.work);
struct wiphy *wiphy = hdd_ctx->wiphy;
int ret;
while (hdd_system_suspend_state(hdd_ctx)) {
hddLog(LOG1, FL("Waiting for system resume complete"));
schedule_timeout_interruptible(100 * HZ / 1000);
}
if (hdd_thermal_suspend_transit(hdd_ctx,
HDD_WLAN_THERMAL_SUSPENDING, NULL)) {
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
ret = __wlan_hdd_cfg80211_suspend_wlan(wiphy, NULL, true);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
if (ret) {
hdd_thermal_suspend_transit(hdd_ctx,
HDD_WLAN_THERMAL_ACTIVE, NULL);
hddLog(LOGE, FL("Thermal suspend failed: %d"), ret);
return;
}
hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_SUSPENDED,
NULL);
} else if (hdd_thermal_suspend_transit(hdd_ctx,
HDD_WLAN_THERMAL_RESUMING, NULL)) {
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
ret = __wlan_hdd_cfg80211_resume_wlan(wiphy, true);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_THERMAL);
if (ret) {
hdd_thermal_suspend_transit(hdd_ctx,
HDD_WLAN_THERMAL_SUSPENDED, NULL);
hddLog(LOGE, FL("Thermal resume failed: %d"), ret);
return;
}
hdd_thermal_suspend_transit(hdd_ctx, HDD_WLAN_THERMAL_ACTIVE,
NULL);
} else {
hddLog(LOGE, FL("Should not reach here"));
}
}
/**
* hdd_thermal_suspend_queue_work() - Queue a thermal suspend work
* @hdd_ctx: Pointer to hdd_context_t
* @ms: Delay time in milliseconds to execute the work
*
* Queue thermal suspend work on the workqueue after delay
*
* Return: false if work was already on a queue, true otherwise.
*/
bool
hdd_thermal_suspend_queue_work(hdd_context_t *hdd_ctx, unsigned long ms)
{
hddLog(LOG1, FL("Queue a thermal suspend work, delay %ld ms"), ms);
return queue_delayed_work(hdd_ctx->thermal_suspend_wq,
&hdd_ctx->thermal_suspend_work, (ms * HZ) / 1000);
}
static void
hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC)
{
if (!hdd_ctx->cfg_ini->thermal_shutdown_auto_enabled) {
return;
}
/*
* Here, we only do thermal suspend.
*
* We can only resume FW elsewhere in two ways:
* 1. triggered by wakeup interrupt from FW when it detects T < Tresume
* 2. user space app launch thermal resume after suspend as app wants
*
* Key factor: FW cannot provide temperature when it suspended.
*/
if ((hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_ACTIVE &&
degreeC >= hdd_ctx->cfg_ini->thermal_suspend_threshold) ||
(hdd_thermal_suspend_state(hdd_ctx) == HDD_WLAN_THERMAL_SUSPENDED &&
degreeC < hdd_ctx->cfg_ini->thermal_resume_threshold)) {
hdd_thermal_suspend_queue_work(hdd_ctx, 0);
}
}
#else
bool
hdd_system_suspend_state_set(hdd_context_t *hdd_ctx, bool state)
{
return TRUE;
}
static inline void
hdd_thermal_suspend_cleanup(hdd_context_t *hdd_ctx)
{
return;
}
static inline void
hdd_thermal_temp_ind_event_cb(hdd_context_t *hdd_ctx, uint32_t degreeC)
{
return;
}
#endif
/**---------------------------------------------------------------------------
\brief hdd_setIbssPowerSaveParams - update IBSS Power Save params to WMA.
This function sets the IBSS power save config parameters to WMA
which will send it to firmware if FW supports IBSS power save
before vdev start.
\param - hdd_adapter_t Hdd adapter.
\return - VOS_STATUS VOS_STATUS_SUCCESS on Success and VOS_STATUS_E_FAILURE
on failure.
--------------------------------------------------------------------------*/
VOS_STATUS hdd_setIbssPowerSaveParams(hdd_adapter_t *pAdapter)
{
int ret;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
if (pHddCtx == NULL) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD context is null", __func__);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE,
pHddCtx->cfg_ini->ibssATIMWinSize,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_ATIM_WINDOW_SIZE failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED,
pHddCtx->cfg_ini->isIbssPowerSaveAllowed,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_POWER_SAVE_ALLOWED failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED,
pHddCtx->cfg_ini->isIbssPowerCollapseAllowed,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_POWER_COLLAPSE_ALLOWED failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX,
pHddCtx->cfg_ini->isIbssAwakeOnTxRx,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_AWAKE_ON_TX_RX failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_INACTIVITY_TIME,
pHddCtx->cfg_ini->ibssInactivityCount,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_INACTIVITY_TIME failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME,
pHddCtx->cfg_ini->ibssTxSpEndInactivityTime,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_SET_TXSP_END_INACTIVITY_TIME failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS,
pHddCtx->cfg_ini->ibssPsWarmupTime,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_PS_SET_WARMUP_TIME_SECS failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
ret = process_wma_set_command(pAdapter->sessionId,
WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW,
pHddCtx->cfg_ini->ibssPs1RxChainInAtimEnable,
VDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("WMA_VDEV_IBSS_PS_SET_1RX_CHAIN_IN_ATIM_WINDOW failed %d"),
ret);
return VOS_STATUS_E_FAILURE;
}
return VOS_STATUS_SUCCESS;
}
#if defined (WLAN_FEATURE_VOWIFI_11R) || defined (FEATURE_WLAN_ESE) || defined(FEATURE_WLAN_LFR)
/**
* hdd_parse_reassoc_command_data() - HDD Parse reassoc command data
* @pValue: Pointer to input data (its a NUL terminated string)
* @pTargetApBssid: Pointer to target Ap bssid
* @pChannel: Pointer to the Target AP channel
*
* This function parses the reasoc command data passed in the format
* REASSOC<space><bssid><space><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;
} else if (SPACE_ASCII_VALUE != *inPtr) {
/*no space after the command*/
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;
/*
* Sigma DUT sends connected bssid and channel 0 to indicate
* driver to issue reassoc to same AP.
* Hence do not treat channel 0 as invalid.
*/
v = kstrtos32(tempBuf, 10, &tempInt);
if ((v < 0) ||
(tempInt < 0) ||
(tempInt > WNI_CFG_CURRENT_CHANNEL_STAMAX)) {
return -EINVAL;
}
*pChannel = tempInt;
return VOS_STATUS_SUCCESS;
}
/**
* 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
*/
int hdd_reassoc(hdd_adapter_t *pAdapter, const tANI_U8 *bssid,
const tANI_U8 channel, const handoff_src src)
{
hdd_station_ctx_t *pHddStaCtx;
int ret = 0;
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(VOS_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
ret = -EINVAL;
goto exit;
}
/* 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(VOS_TRACE_LEVEL_INFO,
"%s: Reassoc BSSID is same as currently associated AP bssid",
__func__);
ret = -EINVAL;
goto exit;
}
/* Check channel number is a valid channel number */
if (VOS_STATUS_SUCCESS !=
wlan_hdd_validate_operation_channel(pAdapter, channel)) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid Channel %d",
__func__, channel);
ret = -EINVAL;
goto exit;
}
/* Proceed with reassoc */
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
{
tCsrHandoffRequest handoffInfo;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
handoffInfo.channel = channel;
handoffInfo.src = src;
memcpy(handoffInfo.bssid, bssid, sizeof(tSirMacAddr));
sme_HandoffRequest(pHddCtx->hHal, pAdapter->sessionId, &handoffInfo);
}
#endif
exit:
return ret;
}
/*
\brief 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
\param - pAdapter - Adapter upon which the command was received
\param - 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(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse reassoc command data", __func__);
} else {
ret = hdd_reassoc(pAdapter, bssid, channel, REASSOC);
}
return ret;
}
/*
\brief 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>
\param - pAdapter - Adapter upon which the command was received
\param - command - command that was received, ASCII command followed
by binary data
\param - 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, "%s: MAC address parsing failed", __func__);
ret = -EINVAL;
} else {
ret = hdd_reassoc(pAdapter, bssid, params.channel, REASSOC);
}
return ret;
}
/*
\brief 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.
\param - pAdapter - Adapter upon which the command was received
\param - command - command that was received
\param - 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;
}
/**
* hdd_parse_send_action_frame_v1_data() - HDD Parse send action frame data
* @pValue: Pointer to input data (its a NUL terminated string)
* @pTargetApBssid: Pointer to target Ap bssid
* @pChannel: Pointer to the Target AP channel
* @pDwellTime: pDwellTime Pointer to the time to stay off-channel
* after transmitting action frame
* @pBuf: Pointer to data
* @pBufLen: Pointer to data length
*
* This function parses the send action frame data passed in the format
* SENDACTIONFRAME<space><bssid><space><channel><space><dwelltime><space><data>
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_send_action_frame_v1_data(const tANI_U8 *pValue,
tANI_U8 *pTargetApBssid,
tANI_U8 *pChannel, tANI_U8 *pDwellTime,
tANI_U8 **pBuf, tANI_U8 *pBufLen)
{
const tANI_U8 *inPtr = pValue;
const 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;
} else if (SPACE_ASCII_VALUE != *inPtr) {
/* no space after the command */
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 0;
}
/*
\brief hdd_sendactionframe() - send a user space supplied action frame
\param - pAdapter - Adapter upon which the command was received
\param - bssid - BSSID target of the action frame
\param - channel - channel upon which to send the frame
\param - dwell_time - amount of time to dwell when the frame is sent
\param - payload_len - length of the payload
\param - payload - payload of the frame
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_sendactionframe(hdd_adapter_t *pAdapter, const tANI_U8 *bssid,
const tANI_U8 channel, const tANI_U8 dwell_time,
const int payload_len, const tANI_U8 *payload)
{
struct ieee80211_channel chan;
int frame_len, ret = 0;
tANI_U8 *frame;
struct ieee80211_hdr_3addr *hdr;
u64 cookie;
hdd_station_ctx_t *pHddStaCtx;
hdd_context_t *pHddCtx;
tpSirMacVendorSpecificFrameHdr pVendorSpecific =
(tpSirMacVendorSpecificFrameHdr) payload;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
struct cfg80211_mgmt_tx_params params;
#endif
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
/* if not associated, no need to send action frame */
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
hddLog(VOS_TRACE_LEVEL_INFO, "%s: Not associated", __func__);
ret = -EINVAL;
goto exit;
}
/* if the target bssid is different from currently associated AP,
then no need to send action frame */
if (memcmp(bssid, pHddStaCtx->conn_info.bssId, VOS_MAC_ADDR_SIZE)) {
hddLog(VOS_TRACE_LEVEL_INFO, "%s: STA is not associated to this AP",
__func__);
ret = -EINVAL;
goto exit;
}
chan.center_freq = sme_ChnToFreq(channel);
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
/* Check if it is specific action frame */
if (pVendorSpecific->category == SIR_MAC_ACTION_VENDOR_SPECIFIC_CATEGORY) {
static const tANI_U8 Oui[] = { 0x00, 0x00, 0xf0 };
if (vos_mem_compare(pVendorSpecific->Oui, (void *) Oui, 3)) {
/* if the channel number is different from operating channel then
no need to send action frame */
if (channel != 0) {
if (channel != pHddStaCtx->conn_info.operationChannel) {
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: channel(%d) is different from operating channel(%d)",
__func__, channel,
pHddStaCtx->conn_info.operationChannel);
ret = -EINVAL;
goto exit;
}
/* If channel number is specified and same as home channel,
* ensure that action frame is sent immediately by cancelling
* roaming scans. Otherwise large dwell times may cause long
* delays in sending action frames.
*/
sme_abortRoamScan(pHddCtx->hHal, pAdapter->sessionId);
} else {
/* 0 is accepted as current home channel, delayed
* transmission of action frame is ok.
*/
chan.center_freq =
sme_ChnToFreq(pHddStaCtx->conn_info.operationChannel);
}
}
}
#endif //#if WLAN_FEATURE_ROAM_SCAN_OFFLOAD
if (chan.center_freq == 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:invalid channel number %d",
__func__, channel);
ret = -EINVAL;
goto exit;
}
frame_len = payload_len + 24;
frame = vos_mem_malloc(frame_len);
if (!frame) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s:memory allocation failed", __func__);
ret = -ENOMEM;
goto exit;
}
vos_mem_zero(frame, frame_len);
hdr = (struct ieee80211_hdr_3addr *)frame;
hdr->frame_control =
cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
vos_mem_copy(hdr->addr1, bssid, VOS_MAC_ADDR_SIZE);
vos_mem_copy(hdr->addr2, pAdapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE);
vos_mem_copy(hdr->addr3, bssid, VOS_MAC_ADDR_SIZE);
vos_mem_copy(hdr + 1, payload, payload_len);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) || defined(WITH_BACKPORTS)
params.chan = &chan;
params.offchan = 0;
params.wait = dwell_time;
params.buf = frame;
params.len = frame_len;
params.no_cck = 1;
params.dont_wait_for_ack = 1;
ret = wlan_hdd_mgmt_tx(NULL, &pAdapter->wdev, &params, &cookie);
#else
ret = wlan_hdd_mgmt_tx(NULL,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS)
&(pAdapter->wdev),
#else
pAdapter->dev,
#endif
&chan, 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)) && !defined(WITH_BACKPORTS)
NL80211_CHAN_HT20, 1,
#endif
dwell_time, frame, frame_len, 1, 1, &cookie );
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)*/
vos_mem_free(frame);
exit:
return ret;
}
/*
\brief hdd_parse_sendactionframe_v1() - parse version 1 of the
SENDACTIONFRAME command
This function parses the v1 SENDACTIONFRAME command with the format
SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DW xxxxxx
Where "xx:xx:xx:xx:xx:xx" is the Hex-ASCII representation of the
BSSID, CH is the ASCII representation of the channel, DW is the
ASCII representation of the dwell time, and xxxxxx is the Hex-ASCII
payload. For example
SENDACTIONFRAME 00:0a:0b:11:22:33 48 40 aabbccddee
\param - pAdapter - Adapter upon which the command was received
\param - command - ASCII text command that was received
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_sendactionframe_v1(hdd_adapter_t *pAdapter, const char *command)
{
tANI_U8 channel = 0;
tANI_U8 dwell_time = 0;
tANI_U8 payload_len = 0;
tANI_U8 *payload = NULL;
tSirMacAddr bssid;
int ret;
ret = hdd_parse_send_action_frame_v1_data(command, bssid, &channel,
&dwell_time, &payload,
&payload_len);
if (ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse send action frame data", __func__);
} else {
ret = hdd_sendactionframe(pAdapter, bssid, channel, dwell_time,
payload_len, payload);
vos_mem_free(payload);
}
return ret;
}
/**
* hdd_parse_sendactionframe_v2() - parse version 2 of the
* SENDACTIONFRAME command
* @pAdapter: Adapter upon which the command was received
* @command: command that was received, ASCII command followed
* by binary data
* @total_len: total length of command
*
* This function parses the v2 SENDACTIONFRAME command with the format
* SENDACTIONFRAME <android_wifi_af_params>
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_sendactionframe_v2(hdd_adapter_t *pAdapter,
const char *command, int total_len)
{
struct android_wifi_af_params *params;
tSirMacAddr bssid;
int ret;
int len_wo_payload = 0;
/* The params are located after "SENDACTIONFRAME " */
total_len -= 16;
len_wo_payload = sizeof(*params) - ANDROID_WIFI_ACTION_FRAME_SIZE;
if (total_len <= len_wo_payload) {
hddLog(LOGE, FL("Invalid command len"));
return -EINVAL;
}
params = (struct android_wifi_af_params *)(command + 16);
if (params->len <= 0 || params->len > ANDROID_WIFI_ACTION_FRAME_SIZE ||
(params->len > (total_len - len_wo_payload))) {
hddLog(LOGE, FL("Invalid payload length: %d"), params->len);
return -EINVAL;
}
if (!mac_pton(params->bssid, (u8 *)&bssid)) {
hddLog(LOGE, FL("MAC address parsing failed"));
return -EINVAL;
}
if (params->channel < 0 ||
params->channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
hddLog(LOGE, FL("Invalid channel: %d"), params->channel);
return -EINVAL;
}
if (params->dwell_time < 0) {
hddLog(LOGE, FL("Invalid dwell_time: %d"), params->dwell_time);
return -EINVAL;
}
ret = hdd_sendactionframe(pAdapter, bssid, params->channel,
params->dwell_time, params->len, params->data);
return ret;
}
/*
\brief hdd_parse_sendactionframe() - parse the SENDACTIONFRAME command
There are two different versions of the SENDACTIONFRAME 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 version of the command.
\param - pAdapter - Adapter upon which the command was received
\param - command - command that was received
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_sendactionframe(hdd_adapter_t *pAdapter, const char *command,
int total_len)
{
int ret;
/* both versions start with "SENDACTIONFRAME "
* v1 has a bssid and other parameters as an ASCII string
* SENDACTIONFRAME xx:xx:xx:xx:xx:xx CH DWELL LEN FRAME
* v2 has a C struct
* SENDACTIONFRAME <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
* SENDACTIONFRAME xx:xx:xx:xx:xx:xx*
* 111111111122222222223333
* 0123456789012345678901234567890123
*
* For both the commands, a valid command must have atleast first 34 length
* of data.
*/
if (total_len < 34) {
hddLog(LOGE, FL("Invalid command (total_len=%d)"), total_len);
return -EINVAL;
}
if (command[33]) {
ret = hdd_parse_sendactionframe_v1(pAdapter, command);
} else {
ret = hdd_parse_sendactionframe_v2(pAdapter, command, total_len);
}
return ret;
}
static 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(LOGW, FL("Invalid Band %d"), band);
*pBand = -1;
break;
}
}
/**
* hdd_parse_channellist() - HDD Parse channel list
* @pValue: Pointer to input data
* @pChannelList: Pointer to input channel list
* @pNumChannels: Pointer to number of roam scan channels
*
* 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
*
* Return: 0 for success non-zero for failure
*/
static int
hdd_parse_channellist(const tANI_U8 *pValue, tANI_U8 *pChannelList,
tANI_U8 *pNumChannels)
{
const 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;
} else if (SPACE_ASCII_VALUE != *inPtr) {
/* no space after the command */
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 0;
} 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 0;
} 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 0;
}
/*
\brief hdd_parse_set_roam_scan_channels_v1() - parse version 1 of the
SETROAMSCANCHANNELS command
This function parses the v1 SETROAMSCANCHANNELS command with the format
SETROAMSCANCHANNELS N C1 C2 ... Cn
Where "N" is the ASCII representation of the number of channels and
C1 thru Cn is the ASCII representation of the channels. For example
SETROAMSCANCHANNELS 4 36 40 44 48
\param - pAdapter - Adapter upon which the command was received
\param - command - ASCII text command that was received
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_set_roam_scan_channels_v1(hdd_adapter_t *pAdapter,
const char *command)
{
tANI_U8 channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U8 num_chan = 0;
eHalStatus status;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int ret;
ret = hdd_parse_channellist(command, channel_list, &num_chan);
if (ret) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse channel list information", __func__);
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
pAdapter->sessionId, num_chan));
if (num_chan > 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__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
ret = -EINVAL;
goto exit;
}
status = sme_ChangeRoamScanChannelList(pHddCtx->hHal, pAdapter->sessionId,
channel_list, num_chan);
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;
}
exit:
return ret;
}
/*
\brief hdd_parse_set_roam_scan_channels_v2() - parse version 2 of the
SETROAMSCANCHANNELS command
This function parses the v2 SETROAMSCANCHANNELS command with the format
SETROAMSCANCHANNELS [N][C1][C2][Cn]
The command begins with SETROAMSCANCHANNELS followed by a space, but
what follows the space is an array of u08 parameters. For example
SETROAMSCANCHANNELS [0x04 0x24 0x28 0x2c 0x30]
\param - pAdapter - Adapter upon which the command was received
\param - command - command that was received, ASCII command followed
by binary data
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_set_roam_scan_channels_v2(hdd_adapter_t *pAdapter,
const char *command)
{
const tANI_U8 *value;
tANI_U8 channel_list[WNI_CFG_VALID_CHANNEL_LIST_LEN] = {0};
tANI_U8 channel;
tANI_U8 num_chan;
int i;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
eHalStatus status;
int ret = 0;
/* array of values begins after "SETROAMSCANCHANNELS " */
value = command + 20;
num_chan = *value++;
if (num_chan > 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__, num_chan, WNI_CFG_VALID_CHANNEL_LIST_LEN);
ret = -EINVAL;
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_SETROAMSCANCHANNELS_IOCTL,
pAdapter->sessionId, num_chan));
for (i = 0; i < num_chan; i++) {
channel = *value++;
if (channel > WNI_CFG_CURRENT_CHANNEL_STAMAX) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: index %d invalid channel %d", __func__, i, channel);
ret = -EINVAL;
goto exit;
}
channel_list[i] = channel;
}
status = sme_ChangeRoamScanChannelList(pHddCtx->hHal, pAdapter->sessionId,
channel_list, num_chan);
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;
}
exit:
return ret;
}
/*
\brief hdd_parse_set_roam_scan_channels() - parse the
SETROAMSCANCHANNELS command
There are two different versions of the SETROAMSCANCHANNELS command.
Version 1 of the command contains a parameter list that is ASCII
characters whereas version 2 contains a 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.
\param - pAdapter - Adapter upon which the command was received
\param - command - command that was received
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int
hdd_parse_set_roam_scan_channels(hdd_adapter_t *pAdapter,
const char *command)
{
const char *cursor;
char ch;
bool v1;
int ret;
/* start after "SETROAMSCANCHANNELS " */
cursor = command + 20;
/* assume we have a version 1 command until proven otherwise */
v1 = true;
/* v1 params will only contain ASCII digits and space */
while ((ch = *cursor++) && v1) {
if (!(isdigit(ch) || isspace(ch))) {
v1 = false;
}
}
if (v1) {
ret = hdd_parse_set_roam_scan_channels_v1(pAdapter, command);
} else {
ret = hdd_parse_set_roam_scan_channels_v2(pAdapter, command);
}
return ret;
}
#endif /* WLAN_FEATURE_VOWIFI_11R || FEATURE_WLAN_ESE || FEATURE_WLAN_LFR */
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
/**---------------------------------------------------------------------------
\brief hdd_parse_plm_cmd() - HDD Parse Plm command
This function parses the plm command passed in the format
CCXPLMREQ<space><enable><space><dialog_token><space>
<meas_token><space><num_of_bursts><space><burst_int><space>
<measu duration><space><burst_len><space><desired_tx_pwr>
<space><multcast_addr><space><number_of_channels>
<space><channel_numbers>
\param - pValue Pointer to input data
\param - pPlmRequest Pointer to output struct tpSirPlmReq
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static eHalStatus hdd_parse_plm_cmd(tANI_U8 *pValue, tSirPlmReq *pPlmRequest)
{
tANI_U8 *cmdPtr = NULL;
int count, content = 0, ret = 0;
char buf[32];
/* moving to argument list */
cmdPtr = strnchr(pValue, strlen(pValue), SPACE_ASCII_VALUE);
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*no space after the command*/
if (SPACE_ASCII_VALUE != *cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* START/STOP PLM req */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
pPlmRequest->enable = content;
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* Dialog token of radio meas req containing meas reqIE */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
pPlmRequest->diag_token = content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "diag token %d", pPlmRequest->diag_token);
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* measurement token of meas req IE */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
pPlmRequest->meas_token = content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "meas token %d", pPlmRequest->meas_token);
hddLog(VOS_TRACE_LEVEL_ERROR,
"PLM req %s", pPlmRequest->enable ? "START" : "STOP");
if (pPlmRequest->enable) {
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* total number of bursts after which STA stops sending */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content < 0)
return eHAL_STATUS_FAILURE;
pPlmRequest->numBursts= content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "num burst %d", pPlmRequest->numBursts);
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/* removing empty spaces */
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* burst interval in seconds */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content <= 0)
return eHAL_STATUS_FAILURE;
pPlmRequest->burstInt = content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "burst Int %d", pPlmRequest->burstInt);
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* Meas dur in TU's,STA goes off-ch and transmit PLM bursts */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content <= 0)
return eHAL_STATUS_FAILURE;
pPlmRequest->measDuration = content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "measDur %d", pPlmRequest->measDuration);
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* burst length of PLM bursts */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content <= 0)
return eHAL_STATUS_FAILURE;
pPlmRequest->burstLen = content;
hddLog(VOS_TRACE_LEVEL_DEBUG, "burstLen %d", pPlmRequest->burstLen);
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* desired tx power for transmission of PLM bursts */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content <= 0)
return eHAL_STATUS_FAILURE;
pPlmRequest->desiredTxPwr = content;
hddLog(VOS_TRACE_LEVEL_DEBUG,
"desiredTxPwr %d", pPlmRequest->desiredTxPwr);
for (count = 0; count < VOS_MAC_ADDR_SIZE; count++)
{
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 16, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
pPlmRequest->macAddr[count] = content;
}
hddLog(VOS_TRACE_LEVEL_DEBUG, "MC addr "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(pPlmRequest->macAddr));
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
/* number of channels */
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if ( ret < 0) return eHAL_STATUS_FAILURE;
if (content < 0)
return eHAL_STATUS_FAILURE;
content = VOS_MIN(content, WNI_CFG_VALID_CHANNEL_LIST_LEN);
pPlmRequest->plmNumCh = content;
hddLog(LOG1, FL("Numch: %d"), pPlmRequest->plmNumCh);
/* Channel numbers */
for (count = 0; count < pPlmRequest->plmNumCh; count++)
{
cmdPtr = strpbrk(cmdPtr, " ");
if (NULL == cmdPtr)
return eHAL_STATUS_FAILURE;
/*removing empty spaces*/
while ((SPACE_ASCII_VALUE == *cmdPtr) && ('\0' != *cmdPtr))
cmdPtr++;
ret = sscanf(cmdPtr, "%31s ", buf);
if (1 != ret) return eHAL_STATUS_FAILURE;
ret = kstrtos32(buf, 10, &content);
if (ret < 0 || content <= 0 ||
content > WNI_CFG_CURRENT_CHANNEL_STAMAX)
return eHAL_STATUS_FAILURE;
pPlmRequest->plmChList[count]= content;
hddLog(VOS_TRACE_LEVEL_DEBUG, " ch- %d",
pPlmRequest->plmChList[count]);
}
} /* If PLM START */
return eHAL_STATUS_SUCCESS;
}
#endif
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
static void wlan_hdd_ready_to_extwow(void *callbackContext,
boolean is_success)
{
hdd_context_t *pHddCtx = (hdd_context_t *)callbackContext;
int rc;
rc = wlan_hdd_validate_context(pHddCtx);
if (0 != rc)
return;
pHddCtx->ext_wow_should_suspend = is_success;
complete(&pHddCtx->ready_to_extwow);
}
static int hdd_enable_ext_wow(hdd_adapter_t *pAdapter,
tpSirExtWoWParams arg_params)
{
tSirExtWoWParams params;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
int rc;
vos_mem_copy(&params, arg_params, sizeof(params));
INIT_COMPLETION(pHddCtx->ready_to_extwow);
halStatus = sme_ConfigureExtWoW(hHal, &params,
&wlan_hdd_ready_to_extwow, pHddCtx);
if (eHAL_STATUS_SUCCESS != halStatus) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ConfigureExtWoW returned failure %d"), halStatus);
return -EPERM;
}
rc = wait_for_completion_timeout(&pHddCtx->ready_to_extwow,
msecs_to_jiffies(WLAN_WAIT_TIME_READY_TO_EXTWOW));
if (!rc) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to get ready to extwow", __func__);
return -EPERM;
}
if (pHddCtx->ext_wow_should_suspend) {
if (pHddCtx->cfg_ini->extWowGotoSuspend) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received ready to ExtWoW. Going to suspend", __func__);
wlan_hdd_cfg80211_suspend_wlan(pHddCtx->wiphy, NULL);
wlan_hif_pci_suspend();
}
} else {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Received ready to ExtWoW failure", __func__);
return -EPERM;
}
return 0;
}
static int hdd_enable_ext_wow_parser(hdd_adapter_t *pAdapter, int vdev_id,
int value)
{
tSirExtWoWParams params;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int rc;
rc = wlan_hdd_validate_context(pHddCtx);
if (0 != rc)
return -EINVAL;
if (value < EXT_WOW_TYPE_APP_TYPE1 || value > EXT_WOW_TYPE_APP_TYPE1_2 ) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Invalid type"));
return -EINVAL;
}
if (value == EXT_WOW_TYPE_APP_TYPE1 &&
pHddCtx->is_extwow_app_type1_param_set)
params.type = value;
else if (value == EXT_WOW_TYPE_APP_TYPE2 &&
pHddCtx->is_extwow_app_type2_param_set)
params.type = value;
else if (value == EXT_WOW_TYPE_APP_TYPE1_2 &&
pHddCtx->is_extwow_app_type1_param_set &&
pHddCtx->is_extwow_app_type2_param_set)
params.type = value;
else {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Set app params before enable it value %d"),value);
return -EINVAL;
}
params.vdev_id = vdev_id;
params.wakeup_pin_num = pHddCtx->cfg_ini->extWowApp1WakeupPinNumber |
(pHddCtx->cfg_ini->extWowApp2WakeupPinNumber << 8);
return hdd_enable_ext_wow(pAdapter, &params);
}
static int hdd_set_app_type1_params(tHalHandle hHal,
tpSirAppType1Params arg_params)
{
tSirAppType1Params params;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
vos_mem_copy(&params, arg_params, sizeof(params));
halStatus = sme_ConfigureAppType1Params(hHal, &params);
if (eHAL_STATUS_SUCCESS != halStatus) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ConfigureAppType1Params returned failure %d"), halStatus);
return -EPERM;
}
return 0;
}
static int hdd_set_app_type1_parser(hdd_adapter_t *pAdapter,
char *arg, int len)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
char id[20], password[20];
tSirAppType1Params params;
int rc, i;
rc = wlan_hdd_validate_context(pHddCtx);
if (0 != rc)
return -EINVAL;
if (2 != sscanf(arg, "%8s %16s", id, password)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid Number of arguments"));
return -EINVAL;
}
memset(&params, 0, sizeof(tSirAppType1Params));
params.vdev_id = pAdapter->sessionId;
for (i = 0; i < ETHER_ADDR_LEN; i++)
params.wakee_mac_addr[i] = pAdapter->macAddressCurrent.bytes[i];
params.id_length = strlen(id);
vos_mem_copy(params.identification_id, id, params.id_length);
params.pass_length = strlen(password);
vos_mem_copy(params.password, password, params.pass_length);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: %d %pM %.8s %u %.16s %u",
__func__, params.vdev_id, params.wakee_mac_addr,
params.identification_id, params.id_length,
params.password, params.pass_length );
return hdd_set_app_type1_params(hHal, &params);
}
static int hdd_set_app_type2_params(tHalHandle hHal,
tpSirAppType2Params arg_params)
{
tSirAppType2Params params;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
vos_mem_copy(&params, arg_params, sizeof(params));
halStatus = sme_ConfigureAppType2Params(hHal, &params);
if (eHAL_STATUS_SUCCESS != halStatus)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("sme_ConfigureAppType2Params returned failure %d"), halStatus);
return -EPERM;
}
return 0;
}
static int hdd_set_app_type2_parser(hdd_adapter_t *pAdapter,
char *arg, int len)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
char mac_addr[20], rc4_key[20];
unsigned int gateway_mac[6], i;
tSirAppType2Params params;
int ret;
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return -EINVAL;
memset(&params, 0, sizeof(tSirAppType2Params));
ret = sscanf(arg, "%17s %16s %x %x %x %u %u %hu %hu %u %u %u %u %u %u",
mac_addr, rc4_key, (unsigned int *)&params.ip_id,
(unsigned int*)&params.ip_device_ip,
(unsigned int*)&params.ip_server_ip,
(unsigned int*)&params.tcp_seq, (unsigned int*)&params.tcp_ack_seq,
(uint16_t*)&params.tcp_src_port,
(uint16_t*)&params.tcp_dst_port,
(unsigned int*)&params.keepalive_init,
(unsigned int*)&params.keepalive_min,
(unsigned int*)&params.keepalive_max,
(unsigned int*)&params.keepalive_inc,
(unsigned int*)&params.tcp_tx_timeout_val,
(unsigned int*)&params.tcp_rx_timeout_val);
if (ret != 15 && ret != 7) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid Number of arguments");
return -EINVAL;
}
if (6 != sscanf(mac_addr, "%02x:%02x:%02x:%02x:%02x:%02x", &gateway_mac[0],
&gateway_mac[1], &gateway_mac[2], &gateway_mac[3],
&gateway_mac[4], &gateway_mac[5])) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid MacAddress Input %s", mac_addr);
return -EINVAL;
}
if (params.tcp_src_port > WLAN_HDD_MAX_TCP_PORT ||
params.tcp_dst_port > WLAN_HDD_MAX_TCP_PORT) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Invalid TCP Port Number");
return -EINVAL;
}
for (i = 0; i < ETHER_ADDR_LEN; i++)
params.gateway_mac[i] = (uint8_t) gateway_mac[i];
params.rc4_key_len = strlen(rc4_key);
vos_mem_copy(params.rc4_key, rc4_key, params.rc4_key_len);
params.vdev_id = pAdapter->sessionId;
params.tcp_src_port = (params.tcp_src_port != 0)?
params.tcp_src_port : pHddCtx->cfg_ini->extWowApp2TcpSrcPort;
params.tcp_dst_port = (params.tcp_dst_port != 0)?
params.tcp_dst_port : pHddCtx->cfg_ini->extWowApp2TcpDstPort;
params.keepalive_init = (params.keepalive_init != 0)?
params.keepalive_init : pHddCtx->cfg_ini->extWowApp2KAInitPingInterval;
params.keepalive_min = (params.keepalive_min != 0)?
params.keepalive_min : pHddCtx->cfg_ini->extWowApp2KAMinPingInterval;
params.keepalive_max = (params.keepalive_max != 0)?
params.keepalive_max : pHddCtx->cfg_ini->extWowApp2KAMaxPingInterval;
params.keepalive_inc = (params.keepalive_inc != 0)?
params.keepalive_inc : pHddCtx->cfg_ini->extWowApp2KAIncPingInterval;
params.tcp_tx_timeout_val = (params.tcp_tx_timeout_val != 0)?
params.tcp_tx_timeout_val : pHddCtx->cfg_ini->extWowApp2TcpTxTimeout;
params.tcp_rx_timeout_val = (params.tcp_rx_timeout_val != 0)?
params.tcp_rx_timeout_val : pHddCtx->cfg_ini->extWowApp2TcpRxTimeout;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: %pM %.16s %u %u %u %u %u %u %u %u %u %u %u %u %u",
__func__, gateway_mac, rc4_key, params.ip_id, params.ip_device_ip,
params.ip_server_ip, params.tcp_seq, params.tcp_ack_seq,
params.tcp_src_port, params.tcp_dst_port, params.keepalive_init,
params.keepalive_min, params.keepalive_max,
params.keepalive_inc, params.tcp_tx_timeout_val,
params.tcp_rx_timeout_val);
return hdd_set_app_type2_params(hHal, &params);
}
#endif
int wlan_hdd_set_mc_rate(hdd_adapter_t *pAdapter, int targetRate)
{
tSirRateUpdateInd rateUpdate = {0};
eHalStatus status;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_config_t *pConfig = NULL;
if (pHddCtx == NULL) {
hddLog(LOGE, FL("HDD context is null"));
return -EINVAL;
}
if ((WLAN_HDD_IBSS != pAdapter->device_mode) &&
(WLAN_HDD_SOFTAP != pAdapter->device_mode) &&
(WLAN_HDD_INFRA_STATION != pAdapter->device_mode)) {
hddLog(LOGE, FL("Received SETMCRATE cmd in invalid device mode %s(%d)"),
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
hddLog(LOGE,
FL("SETMCRATE cmd is allowed only in STA, IBSS or SOFTAP mode"));
return -EINVAL;
}
pConfig = pHddCtx->cfg_ini;
rateUpdate.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
rateUpdate.dev_mode = pAdapter->device_mode;
rateUpdate.mcastDataRate24GHz = targetRate;
rateUpdate.mcastDataRate24GHzTxFlag = 1;
rateUpdate.mcastDataRate5GHz = targetRate;
rateUpdate.bcastDataRate = -1;
memcpy(rateUpdate.bssid, pAdapter->macAddressCurrent.bytes,
sizeof(rateUpdate.bssid));
hddLog(LOG1, FL("MC Target rate %d, mac = %pM, dev_mode %s(%d)"),
rateUpdate.mcastDataRate24GHz, rateUpdate.bssid,
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
status = sme_SendRateUpdateInd(pHddCtx->hHal, &rateUpdate);
if (eHAL_STATUS_SUCCESS != status) {
hddLog(LOGE, FL("SETMCRATE failed"));
return -EFAULT;
}
return 0;
}
/**---------------------------------------------------------------------------
\brief hdd_parse_setmaxtxpower_command() - HDD Parse MAXTXPOWER command
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
\param - pValue Pointer to MAXTXPOWER command
\param - pDbm Pointer to tx power
\return - 0 for success non-zero for failure
--------------------------------------------------------------------------*/
static int hdd_parse_setmaxtxpower_command(tANI_U8 *pValue, int *pTxPower)
{
tANI_U8 *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;
} /*End of hdd_parse_setmaxtxpower_command*/
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 passed 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;
}
int hdd_drv_cmd_validate(tANI_U8 *command, int len)
{
if (command[len] != ' ')
return -EINVAL;
return 0;
}
static int hdd_set_dwell_time(hdd_adapter_t *pAdapter, tANI_U8 *command)
{
tHalHandle hHal;
hdd_config_t *pCfg;
tANI_U8 *value = command;
tSmeConfigParams smeConfig;
int val = 0, ret = 0, temp = 0;
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 passed 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;
}
/**
* 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;
hdd_adapter_t *adapter;
v_CONTEXT_t vos_context;
int i;
/* 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 (HDD_SESSION_ID_ANY == frame_ind->sessionId) {
for (i = 0; i < HDD_SESSION_MAX; i++) {
adapter =
hdd_get_adapter_by_sme_session_id(hdd_ctx, i);
if (adapter)
break;
}
} else {
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->frame_len,
frame_ind->frameBuf,
frame_ind->frameType,
frame_ind->rxChan,
frame_ind->rxRssi);
return;
}
static void hdd_GetLink_statusCB(v_U8_t status, void *pContext)
{
struct statsContext *pLinkContext;
hdd_adapter_t *pAdapter;
if (NULL == pContext) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Bad pContext [%pK]",
__func__, pContext);
return;
}
pLinkContext = pContext;
pAdapter = pLinkContext->pAdapter;
spin_lock(&hdd_context_lock);
if ((NULL == pAdapter) || (LINK_STATUS_MAGIC != pLinkContext->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, pAdapter [%pK] magic [%08x]",
__func__, pAdapter, pLinkContext->magic);
return;
}
/* context is valid so caller is still waiting */
/* paranoia: invalidate the magic */
pLinkContext->magic = 0;
/* copy over the status */
pAdapter->linkStatus = status;
/* notify the caller */
complete(&pLinkContext->completion);
/* serialization is complete */
spin_unlock(&hdd_context_lock);
}
/**
* hdd_get_fw_state_cb() - validates the context and notifies the caller
* @callback_context: caller context
*
* Return: none
*/
static void hdd_get_fw_state_cb(void *callback_context)
{
struct statsContext *context;
hdd_adapter_t *adapter;
if (NULL == callback_context) {
hddLog(LOGE, FL("Bad pContext [%pK]"), callback_context);
return;
}
context = callback_context;
adapter = context->pAdapter;
spin_lock(&hdd_context_lock);
if ((NULL == adapter) || (FW_STATUS_MAGIC != context->magic)) {
/* the caller presumably timed out so there is
* nothing we can do
*/
spin_unlock(&hdd_context_lock);
hddLog(LOGE, FL("Invalid context, Adapter [%pK] magic [%08x]"),
adapter, context->magic);
return;
}
/* context is valid so caller is still waiting */
/* paranoia: invalidate the magic */
context->magic = 0;
/* notify the caller */
complete(&context->completion);
/* serialization is complete */
spin_unlock(&hdd_context_lock);
}
/**
* wlan_hdd_get_fw_state() - get firmware state
* @adapter: pointer to the adapter
*
* This function sends a request to firmware and waits
* on a timer to invoke the callback. if the callback is invoked then
* true will be returned or otherwise fail status will be returned.
*
* Return: true, firmware is active.
* false, firmware is in bad state.
*/
bool wlan_hdd_get_fw_state(hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct statsContext context;
eHalStatus hstatus = eHAL_STATUS_SUCCESS;
unsigned long rc;
bool fw_active = true;
if (wlan_hdd_validate_context(hdd_ctx) != 0)
return false;
init_completion(&context.completion);
context.pAdapter = adapter;
context.magic = FW_STATUS_MAGIC;
hstatus = sme_get_fw_state(WLAN_HDD_GET_HAL_CTX(adapter),
hdd_get_fw_state_cb,
&context);
if (eHAL_STATUS_SUCCESS != hstatus) {
hddLog(LOGE, FL("Unable to retrieve firmware status"));
fw_active = false;
} else {
/* request is sent -- wait for the response */
rc = wait_for_completion_timeout(&context.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
if (!rc) {
hddLog(LOGE,
FL("SME timed out while retrieving firmware status"));
fw_active = false;
}
}
spin_lock(&hdd_context_lock);
context.magic = 0;
spin_unlock(&hdd_context_lock);
return fw_active;
}
/**
* wlan_hdd_get_link_status() - get link status
* @pAdapter: pointer to the adapter
*
* This function sends a request to query the link status and waits
* on a timer to invoke the callback. if the callback is invoked then
* latest link status shall be returned or otherwise cached value
* will be returned.
*
* Return: On success, link status shall be returned.
* On error or not associated, link status 0 will be returned.
*/
static int wlan_hdd_get_link_status(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
struct statsContext context;
eHalStatus hstatus;
unsigned long rc;
if (pHddCtx->isLogpInProgress) {
hddLog(LOGW, FL("LOGP in Progress. Ignore!!!"));
return 0;
}
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
/* If not associated, then expected link status return value is 0 */
hddLog(LOG1, FL("Not associated!"));
return 0;
}
init_completion(&context.completion);
context.pAdapter = pAdapter;
context.magic = LINK_STATUS_MAGIC;
hstatus = sme_getLinkStatus(WLAN_HDD_GET_HAL_CTX(pAdapter),
hdd_GetLink_statusCB,
&context,
pAdapter->sessionId);
if (eHAL_STATUS_SUCCESS != hstatus) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unable to retrieve link status", __func__);
/* return a cached value */
} else {
/* request is sent -- wait for the response */
rc = wait_for_completion_timeout(&context.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_LINK_STATUS));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("SME timed out while retrieving link status"));
}
}
spin_lock(&hdd_context_lock);
context.magic = 0;
spin_unlock(&hdd_context_lock);
/* either callback updated pAdapter stats or it has cached data */
return pAdapter->linkStatus;
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
static void hdd_wma_send_fastreassoc_cmd(int sessionId, tSirMacAddr bssid,
int channel)
{
t_wma_roam_invoke_cmd *fastreassoc;
vos_msg_t msg = {0};
fastreassoc = vos_mem_malloc(sizeof(*fastreassoc));
if (NULL == fastreassoc) {
hddLog(LOGE, FL("vos_mem_alloc failed for fastreassoc"));
return;
}
fastreassoc->vdev_id = sessionId;
fastreassoc->channel = channel;
fastreassoc->bssid[0] = bssid[0];
fastreassoc->bssid[1] = bssid[1];
fastreassoc->bssid[2] = bssid[2];
fastreassoc->bssid[3] = bssid[3];
fastreassoc->bssid[4] = bssid[4];
fastreassoc->bssid[5] = bssid[5];
msg.type = SIR_HAL_ROAM_INVOKE;
msg.reserved = 0;
msg.bodyptr = fastreassoc;
if (VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA,
&msg)) {
vos_mem_free(fastreassoc);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Not able to post ROAM_INVOKE_CMD message to WMA"));
}
}
#endif
/**
* hdd_set_miracast_mode() - function used to set the miracast mode value
* @pAdapter: pointer to the adapter of the interface.
* @command: pointer to the command buffer "MIRACAST <value>".
* Return: 0 on success -EINVAL on failure.
*/
int hdd_set_miracast_mode(hdd_adapter_t *pAdapter, tANI_U8 *command)
{
tHalHandle hHal;
tANI_U8 filterType = 0;
hdd_context_t *pHddCtx = NULL;
tANI_U8 *value;
int ret;
eHalStatus ret_status;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
return -EINVAL;
hHal = pHddCtx->hHal;
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
*/
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: kstrtou8 failed range", __func__);
return -EINVAL;
}
/* Filtertype value should be either 0-Disabled, 1-Source, 2-sink */
if ((filterType < WLAN_HDD_DRIVER_MIRACAST_CFG_MIN_VAL ) ||
(filterType > WLAN_HDD_DRIVER_MIRACAST_CFG_MAX_VAL)) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Accepted Values are 0 to 2."
"0-Disabled, 1-Source, 2-Sink", __func__);
return -EINVAL;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: miracast mode %hu", __func__, filterType);
pHddCtx->miracast_value = filterType;
ret_status = sme_set_miracast(hHal, filterType);
if (eHAL_STATUS_SUCCESS != ret_status) {
hddLog(LOGE, "Failed to set miracast");
return -EBUSY;
}
if (hdd_is_mcc_in_24G(pHddCtx)) {
return hdd_set_mas(pAdapter, filterType);
}
return 0;
}
/* Function header is left blank intentionally */
static int hdd_parse_set_ibss_oui_data_command(uint8_t *command, uint8_t *ie,
int32_t *oui_length, int32_t limit)
{
uint8_t len;
uint8_t data;
while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
command++;
limit--;
}
len = 2;
while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
(limit > 1)) {
sscanf(command, "%02x", (unsigned int *)&data);
ie[len++] = data;
command += 2;
limit -= 2;
}
*oui_length = len - 2;
while ((SPACE_ASCII_VALUE == *command) && ('\0' != *command)) {
command++;
limit--;
}
while ((SPACE_ASCII_VALUE != *command) && ('\0' != *command) &&
(limit > 1)) {
sscanf(command, "%02x", (unsigned int *)&data);
ie[len++] = data;
command += 2;
limit -= 2;
}
ie[0] = IE_EID_VENDOR;
ie[1] = len - 2;
return len;
}
/**
* hdd_set_mas() - Function to set MAS value to UMAC
* @adapter: Pointer to HDD adapter
* @mas_value: 0-Disable, 1-Enable MAS
*
* This function passes down the value of MAS to UMAC
*
* Return: Configuration message posting status, SUCCESS or Fail
*
*/
int32_t hdd_set_mas(hdd_adapter_t *adapter, tANI_U8 mas_value)
{
hdd_context_t *hdd_ctx = NULL;
eHalStatus ret_status;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (!hdd_ctx)
return -EFAULT;
if (mas_value) {
/* Miracast is ON. Disable MAS and configure P2P quota */
if (hdd_ctx->cfg_ini->enableMCCAdaptiveScheduler) {
if (cfgSetInt(hdd_ctx->hHal,
WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, 0)
!= eSIR_SUCCESS) {
hddLog(LOGE,
"Could not pass on WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED to CCM");
}
ret_status = sme_set_mas(false);
if (eHAL_STATUS_SUCCESS != ret_status) {
hddLog(LOGE, "Failed to disable MAS");
return -EBUSY;
}
}
/* Config p2p quota */
if (adapter->device_mode == WLAN_HDD_INFRA_STATION)
hdd_wlan_set_mcc_p2p_quota(adapter,
100 - HDD_DEFAULT_MCC_P2P_QUOTA);
else if (adapter->device_mode == WLAN_HDD_P2P_GO)
hdd_wlan_go_set_mcc_p2p_quota(adapter,
HDD_DEFAULT_MCC_P2P_QUOTA);
else
hdd_wlan_set_mcc_p2p_quota(adapter,
HDD_DEFAULT_MCC_P2P_QUOTA);
} else {
/* Reset p2p quota */
if (adapter->device_mode == WLAN_HDD_P2P_GO)
hdd_wlan_go_set_mcc_p2p_quota(adapter,
HDD_RESET_MCC_P2P_QUOTA);
else
hdd_wlan_set_mcc_p2p_quota(adapter,
HDD_RESET_MCC_P2P_QUOTA);
/* Miracast is OFF. Enable MAS and reset P2P quota */
if (hdd_ctx->cfg_ini->enableMCCAdaptiveScheduler) {
if (cfgSetInt(hdd_ctx->hHal,
WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED, 1)
!= eSIR_SUCCESS) {
hddLog(LOGE, "Could not pass on WNI_CFG_ENABLE_MCC_ADAPTIVE_SCHED to CCM");
}
/* Enable MAS */
ret_status = sme_set_mas(true);
if (eHAL_STATUS_SUCCESS != ret_status) {
hddLog(LOGE, "Unable to enable MAS");
return -EBUSY;
}
}
}
return 0;
}
/**
* hdd_is_mcc_in_24G() - Function to check for MCC in 2.4GHz
* @hdd_ctx: Pointer to HDD context
*
* This function is used to check for MCC operation in 2.4GHz band.
* STA, P2P and SAP adapters are only considered.
*
* Return: Non zero value if MCC is detected in 2.4GHz band
*
*/
uint8_t hdd_is_mcc_in_24G(hdd_context_t *hdd_ctx)
{
VOS_STATUS status;
hdd_adapter_t *hdd_adapter = NULL;
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
uint8_t ret = 0;
hdd_station_ctx_t *sta_ctx;
hdd_ap_ctx_t *ap_ctx;
uint8_t ch1 = 0, ch2 = 0;
uint8_t channel = 0;
hdd_hostapd_state_t *hostapd_state;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
/* loop through all adapters and check MCC for STA,P2P,SAP adapters */
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
hdd_adapter = adapter_node->pAdapter;
if (!((hdd_adapter->device_mode >= WLAN_HDD_INFRA_STATION)
|| (hdd_adapter->device_mode
<= WLAN_HDD_P2P_GO))) {
/* skip for other adapters */
status = hdd_get_next_adapter(hdd_ctx,
adapter_node, &next);
adapter_node = next;
continue;
} else {
if (WLAN_HDD_INFRA_STATION ==
hdd_adapter->device_mode ||
WLAN_HDD_P2P_CLIENT ==
hdd_adapter->device_mode) {
sta_ctx =
WLAN_HDD_GET_STATION_CTX_PTR(
hdd_adapter);
if (eConnectionState_Associated ==
sta_ctx->conn_info.connState)
channel =
sta_ctx->conn_info.
operationChannel;
} else if (WLAN_HDD_P2P_GO ==
hdd_adapter->device_mode ||
WLAN_HDD_SOFTAP ==
hdd_adapter->device_mode) {
ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(hdd_adapter);
hostapd_state =
WLAN_HDD_GET_HOSTAP_STATE_PTR(
hdd_adapter);
if (hostapd_state->bssState == BSS_START &&
hostapd_state->vosStatus ==
VOS_STATUS_SUCCESS)
channel = ap_ctx->operatingChannel;
}
if ((ch1 == 0) ||
((ch2 != 0) && (ch2 != channel))) {
ch1 = channel;
} else if ((ch2 == 0) ||
((ch1 != 0) && (ch1 != channel))) {
ch2 = channel;
}
if ((ch1 != 0 && ch2 != 0) && (ch1 != ch2) &&
((ch1 <= SIR_11B_CHANNEL_END) &&
(ch2 <= SIR_11B_CHANNEL_END))) {
hddLog(LOGE,
"MCC in 2.4Ghz on channels %d and %d",
ch1, ch2);
return 1;
}
status = hdd_get_next_adapter(hdd_ctx,
adapter_node, &next);
adapter_node = next;
}
}
return ret;
}
/**
* wlan_hdd_get_link_speed() - get link speed
* @pAdapter: pointer to the adapter
* @link_speed: pointer to link speed
*
* This function fetches per bssid link speed.
*
* Return: if associated, link speed shall be returned.
* if not associated, link speed of 0 is returned.
* On error, error number will be returned.
*/
int wlan_hdd_get_link_speed(hdd_adapter_t *sta_adapter, uint32_t *link_speed)
{
hdd_context_t *hddctx = WLAN_HDD_GET_CTX(sta_adapter);
hdd_station_ctx_t *hdd_stactx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter);
int ret;
ret = wlan_hdd_validate_context(hddctx);
if (0 != ret)
return ret;
if (eConnectionState_Associated != hdd_stactx->conn_info.connState) {
/* we are not connected so we don't have a classAstats */
*link_speed = 0;
} else {
VOS_STATUS status;
tSirMacAddr bssid;
vos_mem_copy(bssid, hdd_stactx->conn_info.bssId, VOS_MAC_ADDR_SIZE);
status = wlan_hdd_get_linkspeed_for_peermac(sta_adapter, bssid);
if (!VOS_IS_STATUS_SUCCESS(status)) {
hddLog(LOGE, FL("Unable to retrieve SME linkspeed"));
return -EINVAL;
}
*link_speed = sta_adapter->ls_stats.estLinkSpeed;
/* linkspeed in units of 500 kbps */
*link_speed = (*link_speed) / 500;
}
return 0;
}
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
/**
* hdd_parse_ese_beacon_req() - Parse ese beacon request
* @pValue: Pointer to input data (its a NUL terminated string)
* @pEseBcnReq: pEseBcnReq output pointer to store parsed ie information
*
* 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
*
* 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;
} else if (SPACE_ASCII_VALUE != *inPtr) {
/* no space after the command */
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 Number of IE fields */
v = sscanf(inPtr, "%31s ", buf);
if (1 != v) return -EINVAL;
v = kstrtou8(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: %d", 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: %d", 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: %d 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: %d", 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)",
j,
pEseBcnReq->bcnReq[j].measurementToken,
pEseBcnReq->bcnReq[j].channel,
pEseBcnReq->bcnReq[j].scanMode,
pEseBcnReq->bcnReq[j].measurementDuration);
}
return VOS_STATUS_SUCCESS;
}
static void hdd_GetTsmStatsCB( tAniTrafStrmMetrics tsmMetrics,
const tANI_U32 staId,
void *pContext )
{
struct statsContext *pStatsContext = NULL;
hdd_adapter_t *pAdapter = NULL;
if (NULL == pContext) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Bad param, pContext [%pK]",
__func__, pContext);
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);
pStatsContext = pContext;
pAdapter = pStatsContext->pAdapter;
if ((NULL == pAdapter) || (STATS_CONTEXT_MAGIC != pStatsContext->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, pAdapter [%pK] magic [%08x]",
__func__, pAdapter, pStatsContext->magic);
return;
}
/* context is valid so caller is still waiting */
/* paranoia: invalidate the magic */
pStatsContext->magic = 0;
/* copy over the tsm stats */
pAdapter->tsmStats.UplinkPktQueueDly = tsmMetrics.UplinkPktQueueDly;
vos_mem_copy(pAdapter->tsmStats.UplinkPktQueueDlyHist,
tsmMetrics.UplinkPktQueueDlyHist,
sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist)/
sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist[0]));
pAdapter->tsmStats.UplinkPktTxDly = tsmMetrics.UplinkPktTxDly;
pAdapter->tsmStats.UplinkPktLoss = tsmMetrics.UplinkPktLoss;
pAdapter->tsmStats.UplinkPktCount = tsmMetrics.UplinkPktCount;
pAdapter->tsmStats.RoamingCount = tsmMetrics.RoamingCount;
pAdapter->tsmStats.RoamingDly = tsmMetrics.RoamingDly;
/* notify the caller */
complete(&pStatsContext->completion);
/* serialization is complete */
spin_unlock(&hdd_context_lock);
}
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;
unsigned long rc;
struct statsContext context;
hdd_context_t *pHddCtx = NULL;
if (NULL == pAdapter) {
hddLog(LOGE, FL("pAdapter is NULL"));
return VOS_STATUS_E_FAULT;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* we are connected prepare our callback context */
init_completion(&context.completion);
context.pAdapter = pAdapter;
context.magic = STATS_CONTEXT_MAGIC;
/* query tsm stats */
hstatus = sme_GetTsmStats(pHddCtx->hHal, hdd_GetTsmStatsCB,
pHddStaCtx->conn_info.staId[ 0 ],
pHddStaCtx->conn_info.bssId,
&context, 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 */
rc = wait_for_completion_timeout(&context.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_STATS));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME timed out while retrieving statistics",
__func__);
vstatus = VOS_STATUS_E_TIMEOUT;
}
}
/* either we never sent a request, we sent a request and received a
response or we sent a request and timed out. if we never sent a
request or if we sent a request and got a response, we want to
clear the magic out of paranoia. if we timed out there is a
race condition such that the callback function could be
executing at the same time we are. of primary concern is if the
callback function had already verified the "magic" but had not
yet set the completion variable when a timeout occurred. we
serialize these activities by invalidating the magic while
holding a shared spinlock which will cause us to block if the
callback is currently executing */
spin_lock(&hdd_context_lock);
context.magic = 0;
spin_unlock(&hdd_context_lock);
if (VOS_STATUS_SUCCESS == vstatus) {
pTsmMetrics->UplinkPktQueueDly = pAdapter->tsmStats.UplinkPktQueueDly;
vos_mem_copy(pTsmMetrics->UplinkPktQueueDlyHist,
pAdapter->tsmStats.UplinkPktQueueDlyHist,
sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist)/
sizeof(pAdapter->tsmStats.UplinkPktQueueDlyHist[0]));
pTsmMetrics->UplinkPktTxDly = pAdapter->tsmStats.UplinkPktTxDly;
pTsmMetrics->UplinkPktLoss = pAdapter->tsmStats.UplinkPktLoss;
pTsmMetrics->UplinkPktCount = pAdapter->tsmStats.UplinkPktCount;
pTsmMetrics->RoamingCount = pAdapter->tsmStats.RoamingCount;
pTsmMetrics->RoamingDly = pAdapter->tsmStats.RoamingDly;
}
return vstatus;
}
/**
* hdd_parse_get_cckm_ie() - HDD Parse and fetch the CCKM IE
* @pValue: Pointer to input data
* @pCckmIe: Pointer to output cckm Ie
* @pCckmIeLen: Pointer to output cckm ie length
*
* This function parses the SETCCKM IE command
*
* Return: 0 for success non-zero for failure
*/
static 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;
} else if (SPACE_ASCII_VALUE != *inPtr) {
/* no space after the command */
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(LOGP, FL("vos_mem_alloc failed"));
return -ENOMEM;
}
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 */
/**
* drv_cmd_set_fcc_channel() - handle fcc constraint request
* @hdd_ctx: HDD context
* @cmd: command ptr
* @cmd_len: command len
*
* Return: status
*/
static int drv_cmd_set_fcc_channel(hdd_adapter_t *adapter, uint8_t *cmd,
uint8_t cmd_len)
{
uint8_t *value;
uint8_t fcc_constraint;
eHalStatus status;
int ret = 0;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
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(LOGE, FL("value out of range"));
return -EINVAL;
}
status = sme_handle_set_fcc_channel(hdd_ctx->hHal, !fcc_constraint,
adapter->scan_info.mScanPending);
if (status != eHAL_STATUS_SUCCESS)
ret = -EPERM;
return ret;
}
/**
* hdd_set_rx_filter() - set RX filter
* @adapter: Pointer to adapter
* @action: Filter action
* @pattern: Address pattern
*
* Address pattern is most significant byte of address for example
* 0x01 for IPV4 multicast address
* 0x33 for IPV6 multicast address
* 0xFF for broadcast address
*
* Return: 0 for success, non-zero for failure
*/
static int hdd_set_rx_filter(hdd_adapter_t *adapter, bool action,
uint8_t pattern)
{
int ret;
uint8_t i, j;
tHalHandle handle;
tSirRcvFltMcAddrList *filter;
hdd_context_t* hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
handle = hdd_ctx->hHal;
if (NULL == handle) {
hddLog(LOGE, FL("HAL Handle is NULL"));
return -EINVAL;
}
if (!hdd_ctx->cfg_ini->fEnableMCAddrList) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("mc addr ini is disabled"));
return -EINVAL;
}
/*
* If action is false it means start dropping packets
* Set addr_filter_pattern which will be used when sending
* MC/BC address list to target
*/
if (!action)
adapter->addr_filter_pattern = pattern;
else
adapter->addr_filter_pattern = 0;
if (((adapter->device_mode == WLAN_HDD_INFRA_STATION) ||
(adapter->device_mode == WLAN_HDD_P2P_CLIENT)) &&
adapter->mc_addr_list.mc_cnt &&
hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(adapter))) {
filter = vos_mem_malloc(sizeof(*filter));
if (NULL == filter) {
hddLog(LOGE, FL("Could not allocate Memory"));
return -ENOMEM;
}
vos_mem_zero(filter, sizeof(*filter));
filter->action = action;
for (i = 0, j = 0; i < adapter->mc_addr_list.mc_cnt; i++) {
if (!memcmp(&adapter->mc_addr_list.addr[i * ETH_ALEN],
&pattern, 1)) {
memcpy(filter->multicastAddr[j],
&adapter->mc_addr_list.addr[i * ETH_ALEN],
ETH_ALEN);
hddLog(LOG1, "%s RX filter : addr ="
MAC_ADDRESS_STR,
action ? "setting" : "clearing",
MAC_ADDR_ARRAY(filter->multicastAddr[j]));
j++;
}
if (j == SIR_MAX_NUM_MULTICAST_ADDRESS)
break;
}
filter->ulMulticastAddrCnt = j;
/* Set rx filter */
sme_8023MulticastList(handle, adapter->sessionId, filter);
vos_mem_free(filter);
} else {
hddLog(LOGW, FL("mode %d mc_cnt %d"),
adapter->device_mode, adapter->mc_addr_list.mc_cnt);
}
return 0;
}
/**
* hdd_driver_rxfilter_comand_handler() - RXFILTER driver command handler
* @command: Pointer to input string driver command
* @adapter: Pointer to adapter
* @action: Action to enable/disable filtering
*
* If action == false
* Start filtering out data packets based on type
* RXFILTER-REMOVE 0 -> Start filtering out unicast data packets
* RXFILTER-REMOVE 1 -> Start filtering out broadcast data packets
* RXFILTER-REMOVE 2 -> Start filtering out IPV4 mcast data packets
* RXFILTER-REMOVE 3 -> Start filtering out IPV6 mcast data packets
*
* if action == true
* Stop filtering data packets based on type
* RXFILTER-ADD 0 -> Stop filtering unicast data packets
* RXFILTER-ADD 1 -> Stop filtering broadcast data packets
* RXFILTER-ADD 2 -> Stop filtering IPV4 mcast data packets
* RXFILTER-ADD 3 -> Stop filtering IPV6 mcast data packets
*
* Current implementation only supports IPV4 address filtering by
* selectively allowing IPV4 multicast data packest based on
* address list received in .ndo_set_rx_mode
*
* Return: 0 for success, non-zero for failure
*/
static int hdd_driver_rxfilter_comand_handler(uint8_t *command,
hdd_adapter_t *adapter,
bool action)
{
int ret = 0;
uint8_t *value;
uint8_t type;
value = command;
/* Skip space after RXFILTER-REMOVE OR RXFILTER-ADD based on action */
if (!action)
value = command + 16;
else
value = command + 13;
ret = kstrtou8(value, 10, &type);
if (ret < 0) {
hddLog(LOGE,
FL("kstrtou8 failed invalid input value %d"), type);
return -EINVAL;
}
switch (type) {
case 2:
/* Set rx filter for IPV4 multicast data packets */
ret = hdd_set_rx_filter(adapter, action, 0x01);
break;
default:
hddLog(LOG1, FL("Unsupported RXFILTER type %d"), type);
break;
}
return ret;
}
/**
* hdd_parse_setantennamode_command() - HDD Parse SETANTENNAMODE
* command
* @value: Pointer to SETANTENNAMODE command
* @mode: Pointer to antenna mode
*
* This function parses the SETANTENNAMODE command passed in the format
* SETANTENNAMODE<space>mode
*
* Return: 0 for success non-zero for failure
*/
static int hdd_parse_setantennamode_command(const uint8_t *value,
int *mode)
{
const uint8_t *in_ptr = value;
int tmp, v;
char arg1[32];
*mode = 0;
in_ptr = strnchr(value, strlen(value), SPACE_ASCII_VALUE);
/* no argument after the command */
if (NULL == in_ptr) {
hddLog(LOGE, FL("No argument after the command"));
return -EINVAL;
}
/* no space after the command */
if (SPACE_ASCII_VALUE != *in_ptr) {
hddLog(LOGE, FL("No space after the command"));
return -EINVAL;
}
/* remove empty spaces */
while ((SPACE_ASCII_VALUE == *in_ptr) && ('\0' != *in_ptr))
in_ptr++;
/* no argument followed by spaces */
if ('\0' == *in_ptr) {
hddLog(LOGE, FL("No argument followed by spaces"));
return -EINVAL;
}
/* get the argument i.e. antenna mode */
v = sscanf(in_ptr, "%31s ", arg1);
if (1 != v) {
hddLog(LOGE, FL("argument retrieval from cmd string failed"));
return -EINVAL;
}
v = kstrtos32(arg1, 10, &tmp);
if (v < 0) {
hddLog(LOGE, FL("argument string to integer conversion failed"));
return -EINVAL;
}
*mode = tmp;
return 0;
}
/**
* hdd_is_supported_chain_mask_2x2() - Verify if supported chain
* mask is 2x2 mode
* @hdd_ctx: Pointer to hdd contex
*
* Return: true if supported chain mask 2x2 else false
*/
static bool hdd_is_supported_chain_mask_2x2(hdd_context_t *hdd_ctx)
{
hdd_config_t *config = hdd_ctx->cfg_ini;
if (hdd_ctx->per_band_chainmask_supp == 0x01) {
return (((hdd_ctx->supp_2g_chain_mask & 0x03)
== 0x03) ||
((hdd_ctx->supp_5g_chain_mask & 0x03)
== 0x03)) ? true : false;
}
return (config->enable2x2 == 0x01) ? true : false;
}
/**
* hdd_is_supported_chain_mask_1x1() - Verify if the supported
* chain mask is 1x1
* @hdd_ctx: Pointer to hdd contex
*
* Return: true if supported chain mask 1x1 else false
*/
static bool hdd_is_supported_chain_mask_1x1(hdd_context_t *hdd_ctx)
{
hdd_config_t *config = hdd_ctx->cfg_ini;
if (hdd_ctx->per_band_chainmask_supp == 0x01) {
return ((hdd_ctx->supp_2g_chain_mask <= 0x02) &&
(hdd_ctx->supp_5g_chain_mask <= 0x02)) ?
true : false;
}
return (!config->enable2x2) ? true : false;
}
/**
* switch_antenna_mode_non_conn_state() - Dynamic switch to 1x1
* antenna mode when there are no connections
* @hdd_ctx: Pointer to hdd contex
* @adapter: Pointer to hdd adapter
* @chains: Number of TX/RX chains to set
*
* Return: 0 if success else non zero
*/
static int switch_antenna_mode_non_conn_state(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter,
uint8_t chains)
{
int ret;
eHalStatus hal_status;
bool enable_smps;
int smps_mode;
ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx,
(chains == 2) ? 0x3 : 0x1);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to update chain mask: %d"),
chains);
return ret;
}
/* Update HT SMPS as static/disabled in the SME configuration
* If there is STA connection followed by dynamic switch
* to 1x1 protocol stack would include SM power save IE as
* static in the assoc mgmt frame and after association
* SMPS force mode command will be sent to FW to initiate
* SMPS action frames to AP. In this case, SMPS force mode
* command event can be expected from firmware with the
* TX status of SMPS action frames. Inclusion of SM power
* save IE and sending of SMPS action frames will not happen
* for switch to 2x2 mode. But SME config should still be
* updated to disabled.
*/
adapter->smps_force_mode_status = 0;
enable_smps = (chains == 1) ? true : false;
smps_mode = (chains == 1) ? HDD_SMPS_MODE_STATIC :
HDD_SMPS_MODE_DISABLED;
hal_status = sme_update_mimo_power_save(hdd_ctx->hHal,
enable_smps, smps_mode);
if (eHAL_STATUS_SUCCESS != hal_status) {
hddLog(LOG1,
FL("Update MIMO power SME config failed: %d"),
hal_status);
return -EFAULT;
}
hddLog(LOG1, FL("Updated SME config enable smps: %d mode: %d"),
enable_smps, smps_mode);
return 0;
}
/**
* switch_to_1x1_connected_sta_state() - Dynamic switch to 1x1
* antenna mode in standalone station
* @hdd_ctx: Pointer to hdd contex
* @adapter: Pointer to hdd adapter
*
* Return: 0 if success else non zero
*/
static int switch_to_1x1_connected_sta_state(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
int ret;
eHalStatus hal_status;
bool send_smps;
/* Check TDLS status and update antenna mode */
ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter,
HDD_ANTENNA_MODE_1X1);
if (0 != ret)
return ret;
/* If intersection of sta and AP NSS is 1x1 then
* skip SMPS indication to AP. Only update the chain mask
* and other configuration.
*/
send_smps = sme_is_sta_smps_allowed(hdd_ctx->hHal,
adapter->sessionId);
if (!send_smps) {
hddLog(LOGE, FL("Need not indicate SMPS to AP"));
goto chain_mask;
}
INIT_COMPLETION(adapter->smps_force_mode_comp_var);
hddLog(LOG1, FL("Send SMPS force mode command"));
ret = process_wma_set_command((int)adapter->sessionId,
WMI_STA_SMPS_FORCE_MODE_CMDID,
WMI_SMPS_FORCED_MODE_STATIC,
VDEV_CMD);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to send SMPS force mode to static"));
return ret;
}
/* Block on SMPS force mode event only for mode static */
ret = wait_for_completion_timeout(
&adapter->smps_force_mode_comp_var,
msecs_to_jiffies(WLAN_WAIT_SMPS_FORCE_MODE));
if (!ret) {
hddLog(LOGE,
FL("SMPS force mode event timeout: %d"),
ret);
return -EFAULT;
}
ret = adapter->smps_force_mode_status;
adapter->smps_force_mode_status = 0;
if (0 != ret) {
hddLog(LOGE, FL("SMPS force mode status: %d "),
ret);
return ret;
}
chain_mask:
hddLog(LOG1, FL("Update chain mask to 1x1"));
ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, 1);
if (0 != ret) {
hddLog(LOGE, FL("Failed to switch to 1x1 mode"));
return ret;
}
/* Update SME SM power save config */
hal_status = sme_update_mimo_power_save(hdd_ctx->hHal,
true, HDD_SMPS_MODE_STATIC);
if (eHAL_STATUS_SUCCESS != hal_status) {
hddLog(LOG1,
FL("Failed to update SMPS config to static: %d"),
hal_status);
return -EFAULT;
}
hddLog(LOG1, FL("Successfully switched to 1x1 mode"));
return 0;
}
/**
* switch_to_2x2_connected_sta_state() - Dynamic switch to 2x2
* antenna mode in standalone station
* @hdd_ctx: Pointer to hdd contex
* @adapter: Pointer to hdd adapter
*
* Return: 0 if success else non zero
*/
static int switch_to_2x2_connected_sta_state(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
int ret;
eHalStatus hal_status;
bool send_smps;
/* Check TDLS status and update antenna mode */
ret = wlan_hdd_tdls_antenna_switch(hdd_ctx, adapter,
HDD_ANTENNA_MODE_2X2);
if (0 != ret)
return ret;
hddLog(LOG1, FL("Update chain mask to 2x2"));
ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx, 3);
if (0 != ret) {
hddLog(LOGE, FL("Failed to switch to 2x2 mode"));
return ret;
}
/* If intersection of sta and AP NSS is 1x1 then
* skip SMPS indication to AP.
*/
send_smps = sme_is_sta_smps_allowed(hdd_ctx->hHal,
adapter->sessionId);
if (!send_smps) {
hddLog(LOGE, FL("Need not indicate SMPS to AP"));
goto exit;
}
hddLog(LOG1, FL("Send SMPS force mode command "));
/* No need to block on SMPS force mode event when
* the mode switch is 2x2 since the chain mask
* has already been updated to 2x2
*/
ret = process_wma_set_command((int)adapter->sessionId,
WMI_STA_SMPS_FORCE_MODE_CMDID,
WMI_SMPS_FORCED_MODE_DISABLED,
VDEV_CMD);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to send SMPS force mode to disabled"));
return ret;
}
exit:
/* Update SME SM power save config */
hal_status = sme_update_mimo_power_save(hdd_ctx->hHal,
false, HDD_SMPS_MODE_DISABLED);
if (eHAL_STATUS_SUCCESS != hal_status) {
hddLog(LOG1,
FL("Failed to update SMPS config to disable: %d"),
hal_status);
return -EFAULT;
}
hddLog(LOG1, FL("Successfully switched to 2x2 mode"));
return 0;
}
/**
* hdd_decide_dynamic_chain_mask() - Dynamically decide and set chain mask
* for STA
* @hdd_ctx: Pointer to hdd context
* @forced_mode: set to valid mode to force the mode
*
* This function decides the chain mask to set considering number of
* active sessions, type of active sessions and concurrency.
* If forced_mode is valid, chan mask is set to the forced_mode.
*
* Return: void
*/
void hdd_decide_dynamic_chain_mask(hdd_context_t *hdd_ctx,
enum antenna_mode forced_mode)
{
int ret;
hdd_adapter_t *sta_adapter;
hdd_station_ctx_t *hdd_sta_ctx;
enum antenna_mode mode;
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (!hdd_ctx->cfg_ini->enable_dynamic_sta_chainmask ||
!hdd_is_supported_chain_mask_2x2(hdd_ctx)) {
hddLog(LOG1,
FL("dynamic antenna mode in INI is %d or 2x2 not supported"),
hdd_ctx->cfg_ini->enable_dynamic_sta_chainmask);
return;
}
hddLog(LOG1, FL("Current antenna mode: %d"),
hdd_ctx->current_antenna_mode);
if (HDD_ANTENNA_MODE_INVALID != forced_mode) {
mode = forced_mode;
} else if (!wlan_hdd_get_active_session_count(hdd_ctx)) {
/* If no active connection set 1x1 */
mode = HDD_ANTENNA_MODE_1X1;
} else if (1 == wlan_hdd_get_active_session_count(hdd_ctx) &&
hdd_ctx->no_of_active_sessions[WLAN_HDD_INFRA_STATION]) {
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!sta_adapter) {
hddLog(LOGE, FL("Sta adapter null!!"));
return;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter);
if (!hdd_connIsConnected(hdd_sta_ctx)) {
hddLog(LOGE, FL("Sta not connected"));
return;
}
/*
* In case only sta is connected set value depending
* on AP capability.
*/
mode = hdd_sta_ctx->conn_info.nss;
} else {
/* If non sta is active or concurrency set 2x2 */
mode = HDD_ANTENNA_MODE_2X2;
}
if (mode != hdd_ctx->current_antenna_mode) {
ret = wlan_hdd_update_txrx_chain_mask(hdd_ctx,
(HDD_ANTENNA_MODE_2X2 == mode) ? 0x3 : 0x1);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to update chain mask: %d"),
mode);
return;
}
hdd_ctx->current_antenna_mode = mode;
hddLog(LOG1, FL("Antenna mode updated to: %d"),
hdd_ctx->current_antenna_mode);
}
}
/**
* drv_cmd_set_antenna_mode() - SET ANTENNA MODE driver command
* handler
* @hdd_ctx: Pointer to hdd context
* @cmd: Pointer to input command
* @command_len: Command length
*
* Return: 0 for success non-zero for failure
*/
static int drv_cmd_set_antenna_mode(hdd_adapter_t *adapter,
uint8_t *command,
uint8_t cmd_len)
{
int ret;
int mode;
uint8_t *value = command;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if ((hdd_ctx->concurrency_mode > 1) ||
(hdd_ctx->no_of_active_sessions[WLAN_HDD_INFRA_STATION] > 1)) {
hddLog(LOGE, FL("Operation invalid in non sta or concurrent mode"));
ret = -EPERM;
goto exit;
}
ret = hdd_parse_setantennamode_command(value, &mode);
if (0 != ret) {
hddLog(LOGE, FL("Invalid SETANTENNA command"));
goto exit;
}
hddLog(LOG1, FL("Request to switch antenna mode to: %d"), mode);
if (hdd_ctx->current_antenna_mode == mode) {
hddLog(LOGE, FL("System already in the requested mode"));
ret = 0;
goto exit;
}
if ((HDD_ANTENNA_MODE_2X2 == mode) &&
(!hdd_is_supported_chain_mask_2x2(hdd_ctx))) {
hddLog(LOGE, FL("System does not support 2x2 mode"));
ret = -EPERM;
goto exit;
}
if ((HDD_ANTENNA_MODE_1X1 == mode) &&
hdd_is_supported_chain_mask_1x1(hdd_ctx)) {
hddLog(LOGE, FL("System already in 1x1 mode"));
ret = 0;
goto exit;
}
/* Non connected state */
if (0 == wlan_hdd_get_active_session_count(hdd_ctx)) {
hddLog(LOG1,
FL("Switch to %d x %d in non connected state"),
mode, mode);
ret = switch_antenna_mode_non_conn_state(
hdd_ctx, adapter, mode);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to switch to %d x %d mode"),
mode, mode);
goto exit;
}
hdd_ctx->current_antenna_mode = (mode == 1) ?
HDD_ANTENNA_MODE_1X1 : HDD_ANTENNA_MODE_2X2;
} else if ((hdd_ctx->concurrency_mode <= 1) &&
(hdd_ctx->no_of_active_sessions[
WLAN_HDD_INFRA_STATION] <= 1)) {
hddLog(LOG1,
FL("Switch to %d x %d in connected sta state"),
mode, mode);
if (HDD_ANTENNA_MODE_1X1 == mode) {
ret = switch_to_1x1_connected_sta_state(
hdd_ctx, adapter);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to switch to 1x1 mode"));
goto exit;
}
hdd_ctx->current_antenna_mode =
HDD_ANTENNA_MODE_1X1;
} else if (HDD_ANTENNA_MODE_2X2 == mode) {
ret = switch_to_2x2_connected_sta_state(
hdd_ctx, adapter);
if (0 != ret) {
hddLog(LOGE,
FL("Failed to switch to 2x2 mode"));
goto exit;
}
hdd_ctx->current_antenna_mode =
HDD_ANTENNA_MODE_2X2;
}
}
/* Update the user requested nss in the mac context.
* This will be used in tdls protocol engine to form tdls
* Management frames.
*/
sme_update_user_configured_nss(
hdd_ctx->hHal,
hdd_ctx->current_antenna_mode);
exit:
#ifdef FEATURE_WLAN_TDLS
/* Reset tdls NSS flags */
if (hdd_ctx->tdls_nss_switch_in_progress &&
hdd_ctx->tdls_nss_teardown_complete) {
hdd_ctx->tdls_nss_switch_in_progress = false;
hdd_ctx->tdls_nss_teardown_complete = false;
}
hddLog(LOG1,
FL("tdls_nss_switch_in_progress: %d tdls_nss_teardown_complete: %d"),
hdd_ctx->tdls_nss_switch_in_progress,
hdd_ctx->tdls_nss_teardown_complete);
#endif
hddLog(LOG1, FL("Set antenna status: %d current mode: %d"),
ret, hdd_ctx->current_antenna_mode);
return ret;
}
/**
* drv_cmd_get_antenna_mode() - GET ANTENNA MODE driver command
* handler
* @adapter: Pointer to hdd adapter
* @hdd_ctx: Pointer to hdd context
* @command: Pointer to input command
* @command_len: length of the command
* @priv_data: private data coming with the driver command
*
* Return: 0 for success non-zero for failure
*/
static inline int drv_cmd_get_antenna_mode(hdd_adapter_t *adapter,
hdd_context_t *hdd_ctx,
uint8_t *command,
uint8_t command_len,
hdd_priv_data_t *priv_data)
{
uint32_t antenna_mode = 0;
char extra[32];
uint8_t len = 0;
antenna_mode = hdd_ctx->current_antenna_mode;
len = scnprintf(extra, sizeof(extra), "%s %d", command,
antenna_mode);
len = VOS_MIN(priv_data->total_len, len + 1);
if (copy_to_user(priv_data->buf, &extra, len)) {
hddLog(LOGE, FL("Failed to copy data to user buffer"));
return -EFAULT;
}
return 0;
}
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;
int ret = 0;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam()) {
hddLog(LOGE, FL("Command not allowed in FTM mode"));
return -EINVAL;
}
/*
* Note that valid pointers are provided by caller
*/
/* 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;
}
/* Allocate +1 for '\0' */
command = vos_mem_malloc(priv_data.total_len + 1);
if (!command)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to allocate memory", __func__);
ret = -ENOMEM;
goto exit;
}
if (copy_from_user(command, priv_data.buf, priv_data.total_len))
{
ret = -EFAULT;
goto exit;
}
/* Make sure the command is NUL-terminated */
command[priv_data.total_len] = '\0';
/* 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;
hddLog(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)))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: failed to copy data to user buffer", __func__);
ret = -EFAULT;
}
}
else if (strncmp(command, "SETBAND", 7) == 0)
{
tANI_U8 *ptr = command ;
if (hdd_drv_cmd_validate(command, 7))
goto exit;
/* Change band request received */
/* First 8 bytes will have "SETBAND " and
* 9 byte will have band setting value */
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: SetBandCommand Info comm %s UL %d, TL %d",
__func__, command, priv_data.used_len, priv_data.total_len);
/* Change band request received */
ret = hdd_setBand_helper(pAdapter->dev, ptr);
}
else if (strncmp(command, "SETWMMPS", 8) == 0)
{
tANI_U8 *ptr = command;
if (hdd_drv_cmd_validate(command, 8))
goto exit;
ret = hdd_wmmps_helper(pAdapter, ptr);
}
else if (strncasecmp(command, "COUNTRY", 7) == 0)
{
eHalStatus status;
unsigned long rc;
char *country_code;
if (hdd_drv_cmd_validate(command, 7))
goto exit;
country_code = command + 8;
INIT_COMPLETION(pAdapter->change_country_code);
hdd_checkandupdate_dfssetting(pAdapter, country_code);
status =
sme_ChangeCountryCode(pHddCtx->hHal,
(void *)(tSmeChangeCountryCallback)
wlan_hdd_change_country_code_callback,
country_code, pAdapter,
pHddCtx->pvosContext,
eSIR_TRUE, eSIR_TRUE);
if (status == eHAL_STATUS_SUCCESS)
{
rc = wait_for_completion_timeout(
&pAdapter->change_country_code,
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME while setting country code timed out",
__func__);
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME Change Country code fail, status=%d",
__func__, status);
ret = -EINVAL;
}
}
else if (strncmp(command, "SETSUSPENDMODE", 14) == 0)
{
}
#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;
if (hdd_drv_cmd_validate(command, 14))
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(pHddCtx->hHal,
pAdapter->sessionId,
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(pHddCtx->hHal,
pAdapter->sessionId,
lookUpThreshold + 5);
}
else if (strncmp(command, "GETROAMTRIGGER", 14) == 0)
{
tANI_U8 lookUpThreshold =
sme_getNeighborLookupRssiThreshold(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;
if (hdd_drv_cmd_validate(command, 17))
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(pHddCtx->hHal,
pAdapter->sessionId,
neighborEmptyScanRefreshPeriod);
}
else if (strncmp(command, "GETROAMSCANPERIOD", 17) == 0)
{
tANI_U16 nEmptyScanRefreshPeriod =
sme_getEmptyScanRefreshPeriod(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;
if (hdd_drv_cmd_validate(command, 24))
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(pHddCtx->hHal,
pAdapter->sessionId,
neighborScanRefreshPeriod);
}
else if (strncmp(command, "GETROAMSCANREFRESHPERIOD", 24) == 0)
{
tANI_U16 value = sme_getNeighborScanRefreshPeriod(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 (hdd_drv_cmd_validate(command, SIZE_OF_SETROAMMODE))
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, 10, &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_DEBUG,
"%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;
/* LFR2 is dependent on Fast Roam. So, enable/disable LFR2
* variable. if Fast Roam has been changed from disabled to enabled,
* then enable LFR2 and send the LFR START command to the firmware.
* Otherwise, send the LFR STOP command to the firmware and then
* disable LFR2.The sequence is different.
*/
if (roamMode) {
pHddCtx->cfg_ini->isRoamOffloadScanEnabled = roamMode;
sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal),
pHddCtx->cfg_ini->isRoamOffloadScanEnabled);
sme_UpdateIsFastRoamIniFeatureEnabled(pHddCtx->hHal,
pAdapter->sessionId,
roamMode);
} else {
sme_UpdateIsFastRoamIniFeatureEnabled(pHddCtx->hHal,
pAdapter->sessionId,
roamMode);
pHddCtx->cfg_ini->isRoamOffloadScanEnabled = roamMode;
sme_UpdateRoamScanOffloadEnabled((tHalHandle)(pHddCtx->hHal),
pHddCtx->cfg_ini->isRoamOffloadScanEnabled);
}
}
/* GETROAMMODE */
else if (strncmp(command, "GETROAMMODE", SIZE_OF_GETROAMMODE) == 0)
{
tANI_BOOLEAN roamMode = sme_getIsLfrFeatureEnabled(pHddCtx->hHal);
char extra[32];
tANI_U8 len = 0;
/*
* roamMode value shall be inverted because the semantics 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;
if (hdd_drv_cmd_validate(command, 12))
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(pHddCtx->hHal,
pAdapter->sessionId, roamRssiDiff);
}
else if (strncmp(command, "GETROAMDELTA", 12) == 0)
{
tANI_U8 roamRssiDiff = sme_getRoamRssiDiff(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 ", 20) == 0)
{
if (hdd_drv_cmd_validate(command, 20))
goto exit;
ret = hdd_parse_set_roam_scan_channels(pAdapter, command);
}
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(
pHddCtx->hHal,
ChannelList,
&numChannels,
pAdapter->sessionId))
{
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(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(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(pHddCtx->hHal) &&
sme_getIsFtFeatureEnabled(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(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(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;
if (hdd_drv_cmd_validate(command, 25))
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(pHddCtx->hHal,
minTime, pAdapter->sessionId);
}
else if (strncmp(command, "SENDACTIONFRAME", 15) == 0)
{
if (hdd_drv_cmd_validate(command, 15))
goto exit;
ret = hdd_parse_sendactionframe(pAdapter, command,
priv_data.total_len);
}
else if (strncmp(command, "GETROAMSCANCHANNELMINTIME", 25) == 0)
{
tANI_U16 val = sme_getNeighborScanMinChanTime(
pHddCtx->hHal,
pAdapter->sessionId);
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;
if (hdd_drv_cmd_validate(command, 18))
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(pHddCtx->hHal,
pAdapter->sessionId, maxTime);
}
else if (strncmp(command, "GETSCANCHANNELTIME", 18) == 0)
{
tANI_U16 val = sme_getNeighborScanMaxChanTime(pHddCtx->hHal,
pAdapter->sessionId);
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;
if (hdd_drv_cmd_validate(command, 15))
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(pHddCtx->hHal,
pAdapter->sessionId, val);
}
else if (strncmp(command, "GETSCANHOMETIME", 15) == 0)
{
tANI_U16 val = sme_getNeighborScanPeriod(pHddCtx->hHal,
pAdapter->sessionId);
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;
if (hdd_drv_cmd_validate(command, 16))
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(pHddCtx->hHal, val);
}
else if (strncmp(command, "GETROAMINTRABAND", 16) == 0)
{
tANI_U16 val = sme_getRoamIntraBand(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;
if (hdd_drv_cmd_validate(command, 14))
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(pHddCtx->hHal, pAdapter->sessionId,
nProbes);
}
else if (strncmp(command, "GETSCANNPROBES", 14) == 0)
{
tANI_U8 val = sme_getRoamScanNProbes(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;
if (hdd_drv_cmd_validate(command, 19))
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(pHddCtx->hHal,
pAdapter->sessionId,
homeAwayTime, eANI_BOOLEAN_TRUE);
}
}
else if (strncmp(command, "GETSCANHOMEAWAYTIME", 19) == 0)
{
tANI_U16 val = sme_getRoamScanHomeAwayTime(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)
{
if (hdd_drv_cmd_validate(command, 7))
goto exit;
ret = hdd_parse_reassoc(pAdapter, command, priv_data.total_len);
}
else if (strncmp(command, "SETWESMODE", 10) == 0)
{
tANI_U8 *value = command;
tANI_BOOLEAN wesMode = CFG_ENABLE_WES_MODE_NAME_DEFAULT;
if (hdd_drv_cmd_validate(command, 10))
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(pHddCtx->hHal, wesMode, pAdapter->sessionId);
}
else if (strncmp(command, "GETWESMODE", 10) == 0)
{
tANI_BOOLEAN wesMode = sme_GetWESMode(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;
}
}
else if (strncmp(command, "SETOPPORTUNISTICRSSIDIFF", 24) == 0)
{
tANI_U8 *value = command;
tANI_U8 nOpportunisticThresholdDiff = CFG_OPPORTUNISTIC_SCAN_THRESHOLD_DIFF_DEFAULT;
if (hdd_drv_cmd_validate(command, 24))
goto exit;
/* Move pointer to ahead of SETOPPORTUNISTICRSSIDIFF<delimiter> */
value = value + 25;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &nOpportunisticThresholdDiff);
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;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set Opportunistic Threshold diff = %d",
__func__,
nOpportunisticThresholdDiff);
sme_SetRoamOpportunisticScanThresholdDiff(pHddCtx->hHal,
pAdapter->sessionId,
nOpportunisticThresholdDiff);
}
else if (strncmp(command, "GETOPPORTUNISTICRSSIDIFF", 24) == 0)
{
tANI_S8 val = sme_GetRoamOpportunisticScanThresholdDiff(
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, "SETROAMRESCANRSSIDIFF", 21) == 0)
{
tANI_U8 *value = command;
tANI_U8 nRoamRescanRssiDiff = CFG_ROAM_RESCAN_RSSI_DIFF_DEFAULT;
if (hdd_drv_cmd_validate(command, 21))
goto exit;
/* Move pointer to ahead of SETROAMRESCANRSSIDIFF<delimiter> */
value = value + 22;
/* Convert the value from ascii to integer */
ret = kstrtou8(value, 10, &nRoamRescanRssiDiff);
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;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set Roam Rescan RSSI Diff = %d",
__func__,
nRoamRescanRssiDiff);
sme_SetRoamRescanRssiDiff(pHddCtx->hHal,
pAdapter->sessionId,
nRoamRescanRssiDiff);
}
else if (strncmp(command, "GETROAMRESCANRSSIDIFF", 21) == 0)
{
tANI_U8 val = sme_GetRoamRescanRssiDiff(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;
}
}
#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 (hdd_drv_cmd_validate(command, 11))
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(pHddCtx->hHal,
pAdapter->sessionId,
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;
if (hdd_drv_cmd_validate(command, 17))
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(pHddCtx->hHal, ft);
}
else if (strncmp(command, "FASTREASSOC", 11) == 0)
{
tANI_U8 *value = command;
tANI_U8 channel = 0;
tSirMacAddr targetApBssid;
tHalHandle hHal;
v_U32_t roamId = 0;
tCsrRoamModifyProfileFields modProfileFields;
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
tCsrHandoffRequest handoffInfo;
#endif
hdd_station_ctx_t *pHddStaCtx = NULL;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
/* if not associated, no need to proceed with reassoc */
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;
}
ret = hdd_parse_reassoc_command_v1_data(value, targetApBssid,
&channel);
if (ret)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse reassoc command data", __func__);
goto exit;
}
/* if the target bssid is same as currently associated AP,
issue reassoc to same AP */
if (VOS_TRUE == vos_mem_compare(targetApBssid,
pHddStaCtx->conn_info.bssId, sizeof(tSirMacAddr)))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s:Reassoc BSSID is same as currently associated AP bssid",__func__);
sme_GetModifyProfileFields(hHal, pAdapter->sessionId,
&modProfileFields);
sme_RoamReassoc(hHal, pAdapter->sessionId,
NULL, modProfileFields, &roamId, 1);
ret = 0;
goto exit;
}
/* 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);
ret = -EINVAL;
goto exit;
}
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
if (pHddCtx->cfg_ini->isRoamOffloadEnabled) {
hdd_wma_send_fastreassoc_cmd((int)pAdapter->sessionId, targetApBssid,
(int) channel);
goto exit;
}
#endif
/* Proceed with reassoc */
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
handoffInfo.channel = channel;
handoffInfo.src = FASTREASSOC;
vos_mem_copy(handoffInfo.bssid, targetApBssid, sizeof(tSirMacAddr));
sme_HandoffRequest(pHddCtx->hHal, pAdapter->sessionId, &handoffInfo);
#endif
}
#endif
#if defined(FEATURE_WLAN_ESE) && defined(FEATURE_WLAN_ESE_UPLOAD)
else if (strncmp(command, "CCXPLMREQ", 9) == 0)
{
tANI_U8 *value = command;
eHalStatus status = eHAL_STATUS_SUCCESS;
tpSirPlmReq pPlmRequest;
pPlmRequest = vos_mem_malloc(sizeof(tSirPlmReq));
if (NULL == pPlmRequest){
ret = -ENOMEM;
goto exit;
}
status = hdd_parse_plm_cmd(value, pPlmRequest);
if (eHAL_STATUS_SUCCESS != status){
vos_mem_free(pPlmRequest);
pPlmRequest = NULL;
ret = -EINVAL;
goto exit;
}
pPlmRequest->sessionId = pAdapter->sessionId;
status = sme_SetPlmRequest(pHddCtx->hHal, pPlmRequest);
if (eHAL_STATUS_SUCCESS != status)
{
vos_mem_free(pPlmRequest);
pPlmRequest = NULL;
ret = -EINVAL;
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;
if (hdd_drv_cmd_validate(command, 10))
goto exit;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (sme_getIsEseFeatureEnabled(pHddCtx->hHal) &&
hdd_is_okc_mode_enabled(pHddCtx) &&
sme_getIsFtFeatureEnabled(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(pHddCtx->hHal, pAdapter->sessionId,
eseMode);
}
#endif
else if (strncmp(command, "SETROAMSCANCONTROL", 18) == 0)
{
tANI_U8 *value = command;
tANI_BOOLEAN roamScanControl = 0;
if (hdd_drv_cmd_validate(command, 18))
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;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received Command to Set roam scan control = %d", __func__, roamScanControl);
if (0 != roamScanControl)
{
ret = 0; /* return success but ignore param value "TRUE" */
goto exit;
}
sme_SetRoamScanControl(pHddCtx->hHal,
pAdapter->sessionId, roamScanControl);
}
#ifdef FEATURE_WLAN_OKC
else if (strncmp(command, "SETOKCMODE", 10) == 0)
{
tANI_U8 *value = command;
tANI_U8 okcMode = CFG_OKC_FEATURE_ENABLED_DEFAULT;
if (hdd_drv_cmd_validate(command, 10))
goto exit;
/* Check if the features OKC/ESE/11R are supported simultaneously,
then this operation is not permitted (return FAILURE) */
if (sme_getIsEseFeatureEnabled(pHddCtx->hHal) &&
hdd_is_okc_mode_enabled(pHddCtx) &&
sme_getIsFtFeatureEnabled(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, "BTCOEXMODE", 10) == 0 )
{
char *bcMode;
int ret;
if (hdd_drv_cmd_validate(command, 10))
goto exit;
bcMode = command + 11;
if ('1' == *bcMode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
FL("BTCOEXMODE %d"), *bcMode);
pHddCtx->btCoexModeSet = TRUE;
ret = wlan_hdd_scan_abort(pAdapter);
if (ret < 0) {
hddLog(LOGE,
FL("Failed to abort existing scan status:%d"), ret);
}
}
else if ('2' == *bcMode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
FL("BTCOEXMODE %d"), *bcMode);
pHddCtx->btCoexModeSet = FALSE;
}
}
else if (strncmp(command, "GETROAMSCANCONTROL", 18) == 0)
{
tANI_BOOLEAN roamScanControl = sme_GetRoamScanControl(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;
if (hdd_drv_cmd_validate(command, 21))
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 *bcMode;
if (hdd_drv_cmd_validate(command, 10))
goto exit;
bcMode = command + 11;
if ('1' == *bcMode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
FL("BTCOEXMODE %d"), *bcMode);
pHddCtx->btCoexModeSet = TRUE;
}
else if ('2' == *bcMode)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
FL("BTCOEXMODE %d"), *bcMode);
pHddCtx->btCoexModeSet = FALSE;
}
}
else if (strncmp(command, "SCAN-ACTIVE", 11) == 0)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hddLog(LOG1, FL("making default scan to ACTIVE"));
pHddCtx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
}
else if (strncmp(command, "SCAN-PASSIVE", 12) == 0)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hddLog(LOG1, FL("making default scan to PASSIVE"));
pHddCtx->ioctl_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 ( strncasecmp(command, "MIRACAST", 8) == 0 )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Received MIRACAST command", __func__);
if (hdd_drv_cmd_validate(command, 8))
goto exit;
ret = hdd_set_miracast_mode(pAdapter, command);
}
else if ((strncasecmp(command, "SETIBSSBEACONOUIDATA", 20) == 0) &&
(WLAN_HDD_IBSS == pAdapter->device_mode))
{
int i = 0;
uint8_t *ibss_ie;
int32_t command_len;
int32_t oui_length = 0;
uint8_t *value = command;
uint32_t ibss_ie_length;
tSirModifyIE ibssModifyIE;
tCsrRoamProfile *pRoamProfile;
hdd_wext_state_t *pWextState =
WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
int status;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: received command %s", __func__, ((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;
}
if (hdd_drv_cmd_validate(command, 20))
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,
&oui_length, 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;
}
pRoamProfile = &pWextState->roamProfile;
vos_mem_copy(ibssModifyIE.bssid,
pRoamProfile->BSSIDs.bssid,
VOS_MAC_ADDR_SIZE);
ibssModifyIE.smeSessionId = pAdapter->sessionId;
ibssModifyIE.notify = TRUE;
ibssModifyIE.ieID = IE_EID_VENDOR;
ibssModifyIE.ieIDLen = ibss_ie_length;
ibssModifyIE.ieBufferlength = ibss_ie_length;
ibssModifyIE.pIEBuffer = ibss_ie;
ibssModifyIE.oui_length = oui_length;
hddLog(LOGW, FL("ibss_ie length %d oui_length %d ibss_ie:"),
ibss_ie_length, oui_length);
while (i < ibssModifyIE.ieBufferlength) {
hddLog(LOGW, FL("0x%x"), ibss_ie[i++]);
}
/* Probe Bcn modification */
sme_ModifyAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter),
&ibssModifyIE,
eUPDATE_IE_PROBE_BCN);
/* Populating probe resp frame */
sme_ModifyAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter),
&ibssModifyIE,
eUPDATE_IE_PROBE_RESP);
/* free ibss_ie */
vos_mem_free(ibss_ie);
status = sme_SendCesiumEnableInd( (tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId );
if (VOS_STATUS_SUCCESS != status) {
hddLog(LOGE, FL("cesium enable indication failed %d"),
status);
ret = -EINVAL;
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_enable_rmc((tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId);
}
else if(FALSE == ucRmcEnable) {
status = sme_disable_rmc((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;
}
status = sme_SendRmcActionPeriod( (tHalHandle)(pHddCtx->hHal),
pAdapter->sessionId );
if (VOS_STATUS_SUCCESS != status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Could not send cesium enable indication %d", status);
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;
uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
v_U32_t 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 = vos_mem_malloc(WLAN_MAX_BUF_SIZE);
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.numPeers);
numOfBytestoPrint = length;
for (idx = 0; idx < pHddStaCtx->ibss_peer_info.numPeers; idx++) {
int8_t rssi;
uint32_t tx_rate;
vos_mem_copy(mac_addr,
pHddStaCtx->ibss_peer_info.peerInfoParams[idx].mac_addr,
sizeof(mac_addr));
tx_rate = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].txRate;
/* Only lower 3 bytes are rate info. Mask of the MSByte */
tx_rate &= 0x00FFFFFF;
rssi = pHddStaCtx->ibss_peer_info.peerInfoParams[idx].rssi;
length += scnprintf((extra + length),
WLAN_MAX_BUF_SIZE - length,
"%02x:%02x:%02x:%02x:%02x:%02x %d %d ",
mac_addr[0], mac_addr[1], mac_addr[2],
mac_addr[3], mac_addr[4], mac_addr[5],
tx_rate, rssi);
/*
* 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
* more than 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;
goto exit;
}
/* This overwrites the last space, which we already copied */
extra[numOfBytestoPrint - 1] = '\0';
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"%s", extra);
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;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
"%s", &extra[numOfBytestoPrint]);
}
/* Free temporary buffer */
vos_mem_free(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_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 and sanitize it */
hdd_get_peer_sta_id(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.peerInfoParams[0].txRate;
/* Only lower 3 bytes are rate info. Mask of the MSByte */
txRate &= 0x00FFFFFF;
length = scnprintf( extra, sizeof(extra), "%d %d", (int)txRate,
(int)pHddStaCtx->ibss_peer_info.peerInfoParams[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 (strncmp(command, "SETRMCTXRATE", 12) == 0)
{
tANI_U8 *value = command;
tANI_U32 uRate = 0;
tTxrateinfoflags txFlags = 0;
tSirRateUpdateInd rateUpdateParams = {0};
int status;
hdd_config_t *pConfig = pHddCtx->cfg_ini;
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;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: uRate %d ", __func__, uRate);
/* -1 implies ignore this param */
rateUpdateParams.ucastDataRate = -1;
/*
* Fill the user specified RMC rate param
* and the derived tx flags.
*/
rateUpdateParams.nss = (pConfig->enable2x2 == 0) ? 0 : 1;
rateUpdateParams.reliableMcastDataRate = uRate;
rateUpdateParams.reliableMcastDataRateTxFlag = txFlags;
rateUpdateParams.dev_mode = pAdapter->device_mode;
rateUpdateParams.bcastDataRate = -1;
memcpy(rateUpdateParams.bssid, pAdapter->macAddressCurrent.bytes,
sizeof(rateUpdateParams.bssid));
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;
}
}
}
#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;
ret = hdd_parse_channellist(value, ChannelList, &numChannels);
if (ret)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to parse channel list information", __func__);
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(pHddCtx->hHal,
pAdapter->sessionId,
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;
if (hdd_drv_cmd_validate(command, 11))
goto exit;
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* 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)"
"UplinkPktQueueDlyHist[0](%d)"
"UplinkPktQueueDlyHist[1](%d)"
"UplinkPktQueueDlyHist[2](%d)"
"UplinkPktQueueDlyHist[3](%d)"
"UplinkPktTxDly(%u)"
"UplinkPktLoss(%d)"
"UplinkPktCount(%d)"
"RoamingCount(%d)"
"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);
if (NULL != cckmIe)
{
vos_mem_free(cckmIe);
cckmIe = NULL;
}
ret = -EINVAL;
goto exit;
}
sme_SetCCKMIe(pHddCtx->hHal, pAdapter->sessionId, cckmIe, cckmIeLen);
if (NULL != cckmIe)
{
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(pHddCtx->hHal,
pAdapter->sessionId,
&eseBcnReq);
if (eHAL_STATUS_RESOURCES == status) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("sme_SetEseBeaconRequest failed (%d),"
" a request already in progress"), status);
ret = -EBUSY;
goto exit;
} else 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, "SETMCRATE", 9) == 0)
{
tANI_U8 *value = command;
int targetRate;
if (hdd_drv_cmd_validate(command, 9))
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);
ret = wlan_hdd_set_mc_rate(pAdapter, targetRate);
}
else if (strncmp(command, "MAXTXPOWER", 10) == 0)
{
int status;
int txPower;
VOS_STATUS vosStatus;
eHalStatus smeStatus;
tANI_U8 *value = command;
hdd_adapter_t *pAdapter;
tSirMacAddr bssid = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
tSirMacAddr selfMac = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
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;
}
vosStatus = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == vosStatus )
{
pAdapter = pAdapterNode->pAdapter;
/* Assign correct self MAC address */
vos_mem_copy(bssid, pAdapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE);
vos_mem_copy(selfMac, pAdapter->macAddressCurrent.bytes,
VOS_MAC_ADDR_SIZE);
hddLog(VOS_TRACE_LEVEL_INFO, "Device mode %d max tx power %d"
" selfMac: " MAC_ADDRESS_STR " bssId: " MAC_ADDRESS_STR " ",
pAdapter->device_mode, txPower, MAC_ADDR_ARRAY(selfMac),
MAC_ADDR_ARRAY(bssid));
smeStatus = sme_SetMaxTxPower((tHalHandle)(pHddCtx->hHal), bssid,
selfMac, txPower);
if (eHAL_STATUS_SUCCESS != status)
{
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__);
vosStatus = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
}
else if (strncmp(command, "SETDFSSCANMODE", 14) == 0)
{
tANI_U8 *value = command;
tANI_U8 dfsScanMode = CFG_ROAMING_DFS_CHANNEL_DEFAULT;
if (hdd_drv_cmd_validate(command, 14))
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_ROAMING_DFS_CHANNEL_MIN,
CFG_ROAMING_DFS_CHANNEL_MAX);
ret = -EINVAL;
goto exit;
}
if ((dfsScanMode < CFG_ROAMING_DFS_CHANNEL_MIN) ||
(dfsScanMode > CFG_ROAMING_DFS_CHANNEL_MAX))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"dfsScanMode value %d is out of range"
" (Min: %d Max: %d)", dfsScanMode,
CFG_ROAMING_DFS_CHANNEL_MIN,
CFG_ROAMING_DFS_CHANNEL_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);
/* When DFS scanning is disabled, the DFS channels need to be
* removed from the operation of device.
*/
ret = wlan_hdd_disable_dfs_chan_scan(pHddCtx, pAdapter,
(dfsScanMode == CFG_ROAMING_DFS_CHANNEL_DISABLED));
if (ret < 0) {
/* Some conditions prevented it from disabling DFS channels
*/
hddLog(LOGE,
FL("disable/enable DFS channel request was denied"));
goto exit;
}
pHddCtx->cfg_ini->allowDFSChannelRoam = dfsScanMode;
sme_UpdateDFSScanMode(pHddCtx->hHal, pAdapter->sessionId,
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, "GETLINKSTATUS", 13) == 0) {
int value = wlan_hdd_get_link_status(pAdapter);
char extra[32];
tANI_U8 len = 0;
len = scnprintf(extra, sizeof(extra), "%s %d", command, value);
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_EXTWOW_SUPPORT
else if (strncmp(command, "ENABLEEXTWOW", 12) == 0) {
tANI_U8 *value = command;
int set_value;
if (hdd_drv_cmd_validate(command, 12))
goto exit;
/* Move pointer to ahead of ENABLEEXTWOW*/
value += 12;
if (!(sscanf(value, "%d", &set_value))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("No input identified"));
ret = -EINVAL;
goto exit;
}
ret = hdd_enable_ext_wow_parser(pAdapter,
pAdapter->sessionId, set_value);
} else if (strncmp(command, "SETAPP1PARAMS", 13) == 0) {
tANI_U8 *value = command;
if (hdd_drv_cmd_validate(command, 13))
goto exit;
/* Move pointer to ahead of SETAPP1PARAMS*/
value += 13;
ret = hdd_set_app_type1_parser(pAdapter,
value, strlen(value));
if (ret >= 0)
pHddCtx->is_extwow_app_type1_param_set = TRUE;
} else if (strncmp(command, "SETAPP2PARAMS", 13) == 0) {
tANI_U8 *value = command;
if (hdd_drv_cmd_validate(command, 13))
goto exit;
/* Move pointer to ahead of SETAPP2PARAMS*/
value += 13;
ret = hdd_set_app_type2_parser(pAdapter,
value, strlen(value));
if (ret >= 0)
pHddCtx->is_extwow_app_type2_param_set = TRUE;
}
#endif
#ifdef FEATURE_WLAN_TDLS
else if (strncmp(command, "TDLSSECONDARYCHANNELOFFSET", 26) == 0) {
tANI_U8 *value = command;
int set_value;
if (hdd_drv_cmd_validate(command, 26))
goto exit;
/* Move pointer to point the string */
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,
FL("Tdls offchannel offset:%d"),
set_value);
ret = hdd_set_tdls_secoffchanneloffset(pHddCtx, set_value);
} else if (strncmp(command, "TDLSOFFCHANNELMODE", 18) == 0) {
tANI_U8 *value = command;
int set_value;
if (hdd_drv_cmd_validate(command, 18))
goto exit;
/* Move pointer to point the string */
value += 18;
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,
FL("Tdls offchannel mode:%d"),
set_value);
ret = hdd_set_tdls_offchannelmode(pAdapter, set_value);
} else if (strncmp(command, "TDLSOFFCHANNEL", 14) == 0) {
tANI_U8 *value = command;
int set_value;
if (hdd_drv_cmd_validate(command, 14))
goto exit;
/* Move pointer to point the string */
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;
}
if (VOS_IS_DFS_CH(set_value)) {
hddLog(LOGE,
FL("DFS channel %d is passed for hdd_set_tdls_offchannel"),
set_value);
ret = -EINVAL;
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("Tdls offchannel num: %d"), set_value);
ret = hdd_set_tdls_offchannel(pHddCtx, set_value);
} else if (strncmp(command, "TDLSSCAN", 8) == 0) {
uint8_t *value = command;
int set_value;
if (hdd_drv_cmd_validate(command, 8))
goto exit;
/* Move pointer to point the string */
value += 8;
ret = sscanf(value, "%d", &set_value);
if (ret != 1) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"Wrong value is given for tdls_scan_type");
ret = -EINVAL;
goto exit;
}
hddLog(LOG1, FL("Tdls scan type val: %d"),
set_value);
ret = hdd_set_tdls_scan_type(pHddCtx, set_value);
}
#endif
else if (strncasecmp(command, "RSSI", 4) == 0) {
v_S7_t s7Rssi = 0;
char extra[32];
tANI_U8 len = 0;
wlan_hdd_get_rssi(pAdapter, &s7Rssi);
len = scnprintf(extra, sizeof(extra), "%s %d", command, s7Rssi);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
hddLog(LOGE, FL("Failed to copy data to user buffer"));
ret = -EFAULT;
goto exit;
}
} else if (strncasecmp(command, "LINKSPEED", 9) == 0) {
uint32_t link_speed = 0;
char extra[32];
uint8_t len = 0;
ret = wlan_hdd_get_link_speed(pAdapter, &link_speed);
if (0 != ret)
goto exit;
len = scnprintf(extra, sizeof(extra), "%s %d", command, link_speed);
len = VOS_MIN(priv_data.total_len, len + 1);
if (copy_to_user(priv_data.buf, &extra, len)) {
hddLog(LOGE, FL("Failed to copy data to user buffer"));
ret = -EFAULT;
goto exit;
}
} else if (strncasecmp(command, "SET_FCC_CHANNEL", 15) == 0) {
/*
* 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
*/
if (hdd_drv_cmd_validate(command, 15))
goto exit;
ret = drv_cmd_set_fcc_channel(pAdapter, command, 15);
} else if (strncmp(command, "RXFILTER-REMOVE", 15) == 0) {
if (hdd_drv_cmd_validate(command, 15))
goto exit;
ret = hdd_driver_rxfilter_comand_handler(command, pAdapter, false);
} else if (strncmp(command, "RXFILTER-ADD", 12) == 0) {
if (hdd_drv_cmd_validate(command, 12))
goto exit;
ret = hdd_driver_rxfilter_comand_handler(command, pAdapter, true);
} else if (strncasecmp(command, "SETANTENNAMODE", 14) == 0) {
ret = drv_cmd_set_antenna_mode(pAdapter, command, 14);
hddLog(LOG1, FL("set antenna mode ret: %d"), ret);
} else if (strncasecmp(command, "GETANTENNAMODE", 14) == 0) {
ret = drv_cmd_get_antenna_mode(pAdapter, pHddCtx, command,
14, &priv_data);
} else if (strncmp(command, "STOP", 4) == 0) {
hddLog(LOG1, FL("STOP command"));
pHddCtx->driver_being_stopped = true;
} else {
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_UNSUPPORTED_IOCTL,
pAdapter->sessionId, 0));
hddLog( VOS_TRACE_LEVEL_WARN, "%s: Unsupported GUI command %s",
__func__, command);
}
}
exit:
if (command)
{
vos_mem_free(command);
}
EXIT();
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;
}
/**
* __hdd_ioctl() - HDD ioctl handler
* @dev: pointer to net_device structure
* @ifr: pointer to ifreq structure
* @cmd: ioctl command
*
* Return: 0 for success and error number for failure.
*/
static int __hdd_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
long ret = 0;
ENTER();
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))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: invalid data", __func__);
ret = -EINVAL;
goto exit;
}
#if defined(QCA_WIFI_FTM) && defined(LINUX_QCMBR)
if (VOS_FTM_MODE == hdd_get_conparam()) {
if (SIOCIOCTLTX99 == cmd) {
ret = wlan_hdd_qcmbr_unified_ioctl(pAdapter, ifr);
goto exit;
}
}
#endif
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (ret) {
ret = -EBUSY;
goto exit;
}
switch (cmd) {
case (SIOCDEVPRIVATE + 1):
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0)) && defined(CONFIG_X86_64)
if (in_compat_syscall())
#else
if (is_compat_task())
#endif
ret = hdd_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;
}
/**
* hdd_ioctl() - Wrapper function to protect __hdd_ioctl() function from SSR
* @dev: pointer to net_device structure
* @ifr: pointer to ifreq structure
* @cmd: ioctl command
*
* Return: 0 for success and error number for failure.
*/
static 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;
}
/*
* Mac address for multiple virtual interface is found as following
* i) The mac address of the first interface is just the actual hw mac address.
* ii) MSM 3 or 4 bits of byte5 of the actual mac address are used to
* define the mac address for the remaining interfaces and locally
* administered bit is set. INTF_MACADDR_MASK is based on the number of
* supported virtual interfaces, right now this is 0x07 (meaning 8 interface).
* Byte[3] of second interface will be hw_macaddr[3](bit5..7) + 1,
* for third interface it will be hw_macaddr[3](bit5..7) + 2, etc.
*/
void hdd_update_macaddr(hdd_config_t *cfg_ini, v_MACADDR_t hw_macaddr)
{
int8_t i;
u_int8_t macaddr_b3, tmp_br3;
vos_mem_copy(cfg_ini->intfMacAddr[0].bytes, hw_macaddr.bytes,
VOS_MAC_ADDR_SIZE);
for (i = 1; i < VOS_MAX_CONCURRENCY_PERSONA; i++) {
vos_mem_copy(cfg_ini->intfMacAddr[i].bytes, hw_macaddr.bytes,
VOS_MAC_ADDR_SIZE);
macaddr_b3 = cfg_ini->intfMacAddr[i].bytes[3];
tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + i) &
INTF_MACADDR_MASK;
macaddr_b3 += tmp_br3;
/* XOR-ing bit-24 of the mac address. This will give enough
* mac address range before collision
*/
macaddr_b3 ^= (1 << 7);
/* Set locally administered bit */
cfg_ini->intfMacAddr[i].bytes[0] |= 0x02;
cfg_ini->intfMacAddr[i].bytes[3] = macaddr_b3;
hddLog(VOS_TRACE_LEVEL_INFO, "cfg_ini->intfMacAddr[%d]: "
MAC_ADDRESS_STR, i,
MAC_ADDR_ARRAY(cfg_ini->intfMacAddr[i].bytes));
}
}
static void hdd_update_tgt_services(hdd_context_t *hdd_ctx,
struct hdd_tgt_services *cfg)
{
hdd_config_t *cfg_ini = hdd_ctx->cfg_ini;
/* Set up UAPSD */
cfg_ini->apUapsdEnabled &= cfg->uapsd;
#ifdef WLAN_FEATURE_11AC
/* 11AC mode support */
if ((cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac ||
cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY) &&
!cfg->en_11ac)
cfg_ini->dot11Mode = eHDD_DOT11_MODE_AUTO;
#endif /* #ifdef WLAN_FEATURE_11AC */
/* ARP offload: override user setting if invalid */
cfg_ini->fhostArpOffload &= cfg->arp_offload;
#ifdef FEATURE_WLAN_SCAN_PNO
/* PNO offload */
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: PNO Capability in f/w = %d",
__func__,cfg->pno_offload);
if (cfg->pno_offload)
cfg_ini->PnoOffload = TRUE;
#endif
sme_set_lte_coex_supp(hdd_ctx->hHal,
cfg->lte_coex_ant_share);
hdd_ctx->per_band_chainmask_supp = cfg->per_band_chainmask_supp;
sme_set_per_band_chainmask_supp(hdd_ctx->hHal,
cfg->per_band_chainmask_supp);
#ifdef FEATURE_WLAN_TDLS
cfg_ini->fEnableTDLSSupport &= cfg->en_tdls;
cfg_ini->fEnableTDLSOffChannel = cfg_ini->fEnableTDLSOffChannel &&
cfg->en_tdls_offchan;
cfg_ini->fEnableTDLSBufferSta = cfg_ini->fEnableTDLSBufferSta &&
cfg->en_tdls_uapsd_buf_sta;
if (cfg_ini->fTDLSUapsdMask && cfg->en_tdls_uapsd_sleep_sta)
{
cfg_ini->fEnableTDLSSleepSta = TRUE;
}
else
{
cfg_ini->fEnableTDLSSleepSta = FALSE;
}
#endif
sme_set_bcon_offload_supp(hdd_ctx->hHal, cfg->beacon_offload);
#ifdef WLAN_FEATURE_ROAM_OFFLOAD
cfg_ini->isRoamOffloadEnabled &= cfg->en_roam_offload;
#endif
#ifdef SAP_AUTH_OFFLOAD
cfg_ini->enable_sap_auth_offload &= cfg->sap_auth_offload_service;
#endif
cfg_ini->sap_get_peer_info &= cfg->get_peer_info_enabled;
}
/**
* hdd_update_chain_mask_vdev_nss() - sets the chain mask and vdev nss
* @hdd_ctx: HDD context
* @cfg: Pointer to target services.
*
* Sets the chain masks for 2G and 5G bands based on target supported
* values and INI values. And sets the Nss per vdev type based on INI
* and configured chain mask value.
*
* Return: None
*/
static void hdd_update_chain_mask_vdev_nss(hdd_context_t *hdd_ctx,
struct hdd_tgt_services *cfg)
{
hdd_config_t *cfg_ini = hdd_ctx->cfg_ini;
uint8_t chain_mask_rx, chain_mask_tx, ret;
uint8_t max_supp_nss = 1;
cfg_ini->enable2x2 = 0;
chain_mask_rx = cfg->chain_mask_2g & cfg_ini->chain_mask_2g_rx;
chain_mask_tx = cfg->chain_mask_2g & cfg_ini->chain_mask_2g_tx;
if (!chain_mask_rx)
chain_mask_rx = cfg->chain_mask_2g;
if (!chain_mask_tx)
chain_mask_tx = chain_mask_rx;
hddLog(LOG1,
FL("set 2G chain mask rx %d tx %d"),
chain_mask_rx, chain_mask_tx);
ret = process_wma_set_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK_2G,
chain_mask_rx, PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: set WMI_PDEV_PARAM_RX_CHAIN_MASK_2G failed %d",
__func__, ret);
}
ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_2G,
chain_mask_tx, PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_2G set failed %d",
__func__, ret);
}
if (((chain_mask_rx & 0x3) == 0x3) ||
((chain_mask_tx & 0x3) == 0x3))
max_supp_nss++;
if (max_supp_nss == 2)
cfg_ini->enable2x2 = 1;
sme_update_vdev_type_nss(hdd_ctx->hHal, max_supp_nss,
cfg_ini->vdev_type_nss_2g, eCSR_BAND_24);
if (chain_mask_rx >= chain_mask_tx)
hdd_ctx->supp_2g_chain_mask = chain_mask_rx;
else
hdd_ctx->supp_2g_chain_mask = chain_mask_tx;
max_supp_nss = 1;
chain_mask_rx = cfg->chain_mask_5g & cfg_ini->chain_mask_5g_rx;
chain_mask_tx = cfg->chain_mask_5g & cfg_ini->chain_mask_5g_tx;
if (!chain_mask_rx)
chain_mask_rx = cfg->chain_mask_5g;
if (!chain_mask_tx)
chain_mask_tx = chain_mask_rx;
hddLog(LOG1,
FL("set 5G chain mask rx %d tx %d"),
chain_mask_rx, chain_mask_tx);
ret = process_wma_set_command(0, WMI_PDEV_PARAM_RX_CHAIN_MASK_5G,
chain_mask_rx, PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: set WMI_PDEV_PARAM_RX_CHAIN_MASK_5G failed %d",
__func__, ret);
}
ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_5G,
chain_mask_tx, PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_5G set failed %d",
__func__, ret);
}
if (((chain_mask_rx & 0x3) == 0x3) ||
((chain_mask_tx & 0x3) == 0x3))
max_supp_nss++;
if (max_supp_nss == 2)
cfg_ini->enable2x2 = 1;
sme_update_vdev_type_nss(hdd_ctx->hHal, max_supp_nss,
cfg_ini->vdev_type_nss_5g, eCSR_BAND_5G);
if (chain_mask_rx >= chain_mask_tx)
hdd_ctx->supp_5g_chain_mask = chain_mask_rx;
else
hdd_ctx->supp_5g_chain_mask = chain_mask_tx;
hddLog(LOG1, FL("Supported chain mask 2G: %d 5G: %d"),
hdd_ctx->supp_2g_chain_mask,
hdd_ctx->supp_5g_chain_mask);
}
static void hdd_update_tgt_ht_cap(hdd_context_t *hdd_ctx,
struct hdd_tgt_ht_cap *cfg)
{
eHalStatus status;
tANI_U32 value, val32;
tANI_U16 val16;
hdd_config_t *pconfig = hdd_ctx->cfg_ini;
tSirMacHTCapabilityInfo *phtCapInfo;
tANI_U8 mcs_set[SIZE_OF_SUPPORTED_MCS_SET];
uint8_t enable_tx_stbc;
/* check and update RX STBC */
if (pconfig->enableRxSTBC && !cfg->ht_rx_stbc)
pconfig->enableRxSTBC = cfg->ht_rx_stbc;
/* get the MPDU density */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_MPDU_DENSITY, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get MPDU DENSITY",
__func__);
value = 0;
}
/*
* MPDU density:
* override user's setting if value is larger
* than the one supported by target
*/
if (value > cfg->mpdu_density) {
status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_MPDU_DENSITY,
cfg->mpdu_density,
NULL, eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE)
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set MPDU DENSITY to CCM",
__func__);
}
/* get the HT capability info*/
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_HT_CAP_INFO, &val32);
if (eHAL_STATUS_SUCCESS != status) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get HT capability info",
__func__);
return;
}
val16 = (tANI_U16)val32;
phtCapInfo = (tSirMacHTCapabilityInfo *)&val16;
/* Set the LDPC capability */
phtCapInfo->advCodingCap = cfg->ht_rx_ldpc;
if (pconfig->ShortGI20MhzEnable && !cfg->ht_sgi_20)
pconfig->ShortGI20MhzEnable = cfg->ht_sgi_20;
if (pconfig->ShortGI40MhzEnable && !cfg->ht_sgi_40)
pconfig->ShortGI40MhzEnable = cfg->ht_sgi_40;
hdd_ctx->num_rf_chains = cfg->num_rf_chains;
hdd_ctx->ht_tx_stbc_supported = cfg->ht_tx_stbc;
enable_tx_stbc = pconfig->enableTxSTBC;
if (pconfig->enable2x2 && (hdd_ctx->per_band_chainmask_supp ||
(!hdd_ctx->per_band_chainmask_supp && (cfg->num_rf_chains == 2))))
{
pconfig->enable2x2 = 1;
}
else
{
pconfig->enable2x2 = 0;
enable_tx_stbc = 0;
/* 1x1 */
/* Update Rx Highest Long GI data Rate */
if (ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE,
VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE, "Could not pass on "
"WNI_CFG_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE to CCM");
}
/* Update Tx Highest Long GI data Rate */
if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_HIGHEST_SUPPORTED_DATA_RATE,
VHT_TX_HIGHEST_SUPPORTED_DATA_RATE_1_1, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE)
{
hddLog(LOGE, "Could not pass on "
"HDD_VHT_RX_HIGHEST_SUPPORTED_DATA_RATE_1_1 to CCM");
}
}
if (!(cfg->ht_tx_stbc && pconfig->enable2x2))
{
enable_tx_stbc = 0;
}
phtCapInfo->txSTBC = enable_tx_stbc;
val32 = val16;
status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_HT_CAP_INFO,
val32, NULL, eANI_BOOLEAN_FALSE);
if (status != eHAL_STATUS_SUCCESS)
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set HT capability to CCM",
__func__);
#define WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES 0xff
value = SIZE_OF_SUPPORTED_MCS_SET;
if (ccmCfgGetStr(hdd_ctx->hHal, WNI_CFG_SUPPORTED_MCS_SET, mcs_set,
&value) == eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s: Read MCS rate set", __func__);
if (pconfig->enable2x2)
{
for (value = 0; value < 2; value++)
mcs_set[value] = WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES;
status = ccmCfgSetStr(hdd_ctx->hHal, WNI_CFG_SUPPORTED_MCS_SET,
mcs_set, SIZE_OF_SUPPORTED_MCS_SET, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE)
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set MCS SET to CCM", __func__);
}
}
#undef WLAN_HDD_RX_MCS_ALL_NSTREAM_RATES
}
#ifdef WLAN_FEATURE_11AC
static void hdd_update_tgt_vht_cap(hdd_context_t *hdd_ctx,
struct hdd_tgt_vht_cap *cfg)
{
eHalStatus status;
tANI_U32 value = 0;
hdd_config_t *pconfig = hdd_ctx->cfg_ini;
tANI_U32 temp = 0;
/* Get the current MPDU length */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MAX_MPDU_LENGTH, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get MPDU LENGTH",
__func__);
value = 0;
}
/*
* VHT max MPDU length:
* override if user configured value is too high
* that the target cannot support
*/
if (value > cfg->vht_max_mpdu) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_MAX_MPDU_LENGTH,
cfg->vht_max_mpdu, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT MAX MPDU LENGTH",
__func__);
}
}
/* Get the current supported chan width */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SUPPORTED_CHAN_WIDTH_SET,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get MPDU LENGTH",
__func__);
value = 0;
}
/*
* Update VHT supported chan width:
* if user setting is invalid, override it with
* target capability
*/
if ((value == eHT_CHANNEL_WIDTH_80MHZ &&
!(cfg->supp_chan_width & eHT_CHANNEL_WIDTH_80MHZ)) ||
(value == eHT_CHANNEL_WIDTH_160MHZ &&
!(cfg->supp_chan_width & eHT_CHANNEL_WIDTH_160MHZ))) {
u_int32_t width = eHT_CHANNEL_WIDTH_20MHZ;
if (cfg->supp_chan_width & eHT_CHANNEL_WIDTH_160MHZ)
width = eHT_CHANNEL_WIDTH_160MHZ;
else if (cfg->supp_chan_width & eHT_CHANNEL_WIDTH_80MHZ)
width = eHT_CHANNEL_WIDTH_80MHZ;
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SUPPORTED_CHAN_WIDTH_SET,
width, NULL, eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT SUPPORTED CHAN WIDTH",
__func__);
}
}
ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_BASIC_MCS_SET, &temp);
temp = (temp & VHT_MCS_1x1) | pconfig->vhtRxMCS;
if (pconfig->enable2x2)
temp = (temp & VHT_MCS_2x2) | (pconfig->vhtRxMCS2x2 << 2);
if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_BASIC_MCS_SET, temp, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) {
hddLog(LOGE, "Could not pass on WNI_CFG_VHT_BASIC_MCS_SET to CCM");
}
ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_RX_MCS_MAP, &temp);
temp = (temp & VHT_MCS_1x1) | pconfig->vhtRxMCS;
if (pconfig->enable2x2)
temp = (temp & VHT_MCS_2x2) | (pconfig->vhtRxMCS2x2 << 2);
if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_RX_MCS_MAP, temp, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) {
hddLog(LOGE, "Could not pass on WNI_CFG_VHT_RX_MCS_MAP to CCM");
}
ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_MCS_MAP, &temp);
temp = (temp & VHT_MCS_1x1) | pconfig->vhtTxMCS;
if (pconfig->enable2x2)
temp = (temp & VHT_MCS_2x2) | (pconfig->vhtTxMCS2x2 << 2);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"vhtRxMCS2x2 - %x temp - %u enable2x2 %d",
pconfig->vhtRxMCS2x2, temp, pconfig->enable2x2);
if (ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TX_MCS_MAP, temp, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) {
hddLog(LOGE, "Could not pass on WNI_CFG_VHT_TX_MCS_MAP to CCM");
}
/* Get the current RX LDPC setting */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_LDPC_CODING_CAP, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT LDPC CODING CAP",
__func__);
value = 0;
}
/* Set the LDPC capability */
if (value && !cfg->vht_rx_ldpc) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_LDPC_CODING_CAP,
cfg->vht_rx_ldpc, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT LDPC CODING CAP to CCM",
__func__);
}
}
/* Get current GI 80 value */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SHORT_GI_80MHZ, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get SHORT GI 80MHZ",
__func__);
value = 0;
}
/* set the Guard interval 80MHz */
if (value && !cfg->vht_short_gi_80) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SHORT_GI_80MHZ,
cfg->vht_short_gi_80, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set SHORT GI 80MHZ to CCM",
__func__);
}
}
/* Get current GI 160 value */
status = ccmCfgGetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SHORT_GI_160_AND_80_PLUS_80MHZ,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get SHORT GI 80 & 160",
__func__);
value = 0;
}
/* set the Guard interval 160MHz */
if (value && !cfg->vht_short_gi_160) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SHORT_GI_160_AND_80_PLUS_80MHZ,
cfg->vht_short_gi_160, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set SHORT GI 80 & 160 to CCM",
__func__);
}
}
/* Get VHT TX STBC cap */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXSTBC, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT TX STBC",
__func__);
value = 0;
}
/* VHT TX STBC cap */
if (value && !cfg->vht_tx_stbc) {
status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXSTBC,
cfg->vht_tx_stbc, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set the VHT TX STBC to CCM",
__func__);
}
}
/* Get VHT RX STBC cap */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_RXSTBC, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT RX STBC",
__func__);
value = 0;
}
/* VHT RX STBC cap */
if (value && !cfg->vht_rx_stbc) {
status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_RXSTBC,
cfg->vht_rx_stbc, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set the VHT RX STBC to CCM",
__func__);
}
}
/* Get VHT SU Beamformer cap */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_SU_BEAMFORMER_CAP,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT SU BEAMFORMER CAP",
__func__);
value = 0;
}
/* set VHT SU Beamformer cap */
if (value && !cfg->vht_su_bformer) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SU_BEAMFORMER_CAP,
cfg->vht_su_bformer, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT SU BEAMFORMER CAP",
__func__);
}
}
/* check and update SU BEAMFORMEE capability*/
if (pconfig->enableTxBF && !cfg->vht_su_bformee)
pconfig->enableTxBF = cfg->vht_su_bformee;
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_SU_BEAMFORMEE_CAP,
pconfig->enableTxBF, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT SU BEAMFORMEE CAP",
__func__);
}
/* Get VHT MU Beamformer cap */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MU_BEAMFORMER_CAP,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT MU BEAMFORMER CAP",
__func__);
value = 0;
}
/* set VHT MU Beamformer cap */
if (value && !cfg->vht_mu_bformer) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_MU_BEAMFORMER_CAP,
cfg->vht_mu_bformer, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set the VHT MU BEAMFORMER CAP to CCM",
__func__);
}
}
/* Get VHT MU Beamformee cap */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_MU_BEAMFORMEE_CAP,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT MU BEAMFORMEE CAP",
__func__);
value = 0;
}
/* check and update MU BEAMFORMEE capability*/
if (pconfig->enableMuBformee && !cfg->vht_mu_bformee)
pconfig->enableMuBformee = cfg->vht_mu_bformee;
/* set VHT MU Beamformee cap */
if (value && !cfg->vht_mu_bformee) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_MU_BEAMFORMEE_CAP,
pconfig->enableMuBformee, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set VHT MU BEAMFORMER CAP",
__func__);
}
}
/* Get VHT MAX AMPDU Len exp */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_AMPDU_LEN_EXPONENT,
&value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT AMPDU LEN",
__func__);
value = 0;
}
/*
* VHT max AMPDU len exp:
* override if user configured value is too high
* that the target cannot support.
* Even though Rome publish ampdu_len=7, it can
* only support 4 because of some h/w bug.
*/
if (value > cfg->vht_max_ampdu_len_exp) {
status = ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_AMPDU_LEN_EXPONENT,
cfg->vht_max_ampdu_len_exp, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set the VHT AMPDU LEN EXP",
__func__);
}
}
/* Get VHT TXOP PS CAP */
status = ccmCfgGetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXOP_PS, &value);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s: could not get VHT TXOP PS",
__func__);
value = 0;
}
/* set VHT TXOP PS cap */
if (value && !cfg->vht_txop_ps) {
status = ccmCfgSetInt(hdd_ctx->hHal, WNI_CFG_VHT_TXOP_PS,
cfg->vht_txop_ps, NULL,
eANI_BOOLEAN_FALSE);
if (status == eHAL_STATUS_FAILURE) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"%s: could not set the VHT TXOP PS",
__func__);
}
}
hddLog(LOG1, "enable2x2 %d ", pconfig->enable2x2);
if (pconfig->enable2x2)
{
if (ccmCfgSetInt(hdd_ctx->hHal,
WNI_CFG_VHT_NUM_SOUNDING_DIMENSIONS,
NUM_OF_SOUNDING_DIMENSIONS, NULL,
eANI_BOOLEAN_FALSE) == eHAL_STATUS_FAILURE) {
hddLog(LOGE,
"Could not set WNI_CFG_VHT_NUM_SOUNDING_DIMENSIONS to CCM");
}
}
}
#endif /* #ifdef WLAN_FEATURE_11AC */
#ifdef FEATURE_WLAN_RA_FILTERING
static void hdd_update_ra_rate_limit(hdd_context_t *hdd_ctx,
struct hdd_tgt_cfg *cfg)
{
hdd_ctx->cfg_ini->IsRArateLimitEnabled = cfg->is_ra_rate_limit_enabled;
}
#else
static void hdd_update_ra_rate_limit(hdd_context_t *hdd_ctx,
struct hdd_tgt_cfg *cfg)
{
}
#endif
void hdd_update_tgt_cfg(void *context, void *param)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)context;
struct hdd_tgt_cfg *cfg = (struct hdd_tgt_cfg *)param;
tANI_U8 temp_band_cap;
if (hdd_cfg_is_sub20_channel_width_enabled(hdd_ctx) &&
cfg->sub_20_support == 0) {
hddLog(VOS_TRACE_LEVEL_WARN,
FL("requests 5/10M, target not support"));
hdd_ctx->cfg_ini->sub_20_channel_width = 0;
}
/* first store the INI band capability */
temp_band_cap = hdd_ctx->cfg_ini->nBandCapability;
hdd_ctx->cfg_ini->nBandCapability = cfg->band_cap;
/* now overwrite the target band capability with INI
setting if INI setting is a subset */
if ((hdd_ctx->cfg_ini->nBandCapability == eCSR_BAND_ALL) &&
(temp_band_cap != eCSR_BAND_ALL))
hdd_ctx->cfg_ini->nBandCapability = temp_band_cap;
else if ((hdd_ctx->cfg_ini->nBandCapability != eCSR_BAND_ALL) &&
(temp_band_cap != eCSR_BAND_ALL) &&
(hdd_ctx->cfg_ini->nBandCapability != temp_band_cap)) {
hddLog(VOS_TRACE_LEVEL_WARN,
FL("ini BandCapability not supported by the target"));
}
if (!hdd_ctx->isLogpInProgress) {
hdd_ctx->reg.reg_domain = cfg->reg_domain;
hdd_ctx->reg.eeprom_rd_ext = cfg->eeprom_rd_ext;
}
/* This can be extended to other configurations like ht, vht cap... */
if (!vos_is_macaddr_zero(&cfg->hw_macaddr))
{
vos_mem_copy(&hdd_ctx->hw_macaddr, &cfg->hw_macaddr,
VOS_MAC_ADDR_SIZE);
}
else {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: HW MAC is zero", __func__);
}
hdd_ctx->target_fw_version = cfg->target_fw_version;
hdd_ctx->max_intf_count = cfg->max_intf_count;
#ifdef WLAN_FEATURE_LPSS
hdd_ctx->lpss_support = cfg->lpss_support;
#endif
wlan_hdd_set_egap_support(hdd_ctx, cfg);
hdd_ctx->ap_arpns_support = cfg->ap_arpns_support;
hdd_update_tgt_services(hdd_ctx, &cfg->services);
if (hdd_ctx->per_band_chainmask_supp)
hdd_update_chain_mask_vdev_nss(hdd_ctx, &cfg->services);
else
sme_set_vdev_nss(hdd_ctx->hHal, hdd_ctx->cfg_ini->enable2x2);
hdd_update_tgt_ht_cap(hdd_ctx, &cfg->ht_cap);
#ifdef WLAN_FEATURE_11AC
hdd_update_tgt_vht_cap(hdd_ctx, &cfg->vht_cap);
#endif /* #ifdef WLAN_FEATURE_11AC */
hdd_ctx->current_antenna_mode =
hdd_is_supported_chain_mask_2x2(hdd_ctx) ?
HDD_ANTENNA_MODE_2X2 : HDD_ANTENNA_MODE_1X1;
hddLog(LOG1, FL("Current antenna mode: %d"),
hdd_ctx->current_antenna_mode);
hdd_ctx->cfg_ini->fine_time_meas_cap &= cfg->fine_time_measurement_cap;
hdd_ctx->fine_time_meas_cap_target = cfg->fine_time_measurement_cap;
hddLog(LOG1, FL("fine_time_measurement_cap: 0x%x"),
hdd_ctx->cfg_ini->fine_time_meas_cap);
hddLog(LOG1, FL("Target BPF %d Host BPF %d"),
cfg->bpf_enabled, hdd_ctx->cfg_ini->bpf_packet_filter_enable);
hdd_ctx->bpf_enabled = (cfg->bpf_enabled &&
hdd_ctx->cfg_ini->bpf_packet_filter_enable);
hdd_update_ra_rate_limit(hdd_ctx, cfg);
/*
* If BPF is enabled, maxWowFilters set to WMA_STA_WOW_DEFAULT_PTRN_MAX
* because we need atleast WMA_STA_WOW_DEFAULT_PTRN_MAX free slots to
* configure the STA mode wow pattern.
*/
if (hdd_ctx->bpf_enabled)
hdd_ctx->cfg_ini->maxWoWFilters = WMA_STA_WOW_DEFAULT_PTRN_MAX;
hdd_ctx->wmi_max_len = cfg->wmi_max_len;
/* Configure NAN datapath features */
hdd_nan_datapath_target_config(hdd_ctx, cfg);
hdd_ctx->max_mc_addr_list = cfg->max_mc_addr_list;
}
void hdd_update_dfs_cac_block_tx_flag(void *context, bool cac_block_tx)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)context;
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
hdd_adapter_t *adapter;
VOS_STATUS status;
if (wlan_hdd_validate_context(hdd_ctx))
return;
if (hdd_ctx->cfg_ini->disableDFSChSwitch)
return;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if (WLAN_HDD_SOFTAP == adapter->device_mode ||
WLAN_HDD_P2P_GO == adapter->device_mode)
WLAN_HDD_GET_AP_CTX_PTR(adapter)->dfs_cac_block_tx =
cac_block_tx;
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
}
/* This function is invoked in atomic context when a radar
* is found on the SAP current operating channel and Data
* Tx from netif has to be stopped to honor the DFS regulations.
* Actions: Stop the netif Tx queues,Indicate Radar present
* in HDD context for future usage.
*/
bool hdd_dfs_indicate_radar(void *context, void *param)
{
hdd_context_t *pHddCtx= (hdd_context_t *)context;
struct hdd_dfs_radar_ind *hdd_radar_event =
(struct hdd_dfs_radar_ind*)param;
hdd_adapter_list_node_t *pAdapterNode = NULL,
*pNext = NULL;
hdd_adapter_t *pAdapter;
VOS_STATUS status;
if (!pHddCtx || !hdd_radar_event || pHddCtx->cfg_ini->disableDFSChSwitch)
return true;
if (VOS_TRUE == hdd_radar_event->dfs_radar_status)
{
SPIN_LOCK_BH(&pHddCtx->dfs_lock);
if (pHddCtx->dfs_radar_found)
{
/* Application already triggered channel switch
* on current channel, so return here
*/
SPIN_UNLOCK_BH(&pHddCtx->dfs_lock);
return false;
}
pHddCtx->dfs_radar_found = VOS_TRUE;
SPIN_UNLOCK_BH(&pHddCtx->dfs_lock);
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
if (WLAN_HDD_SOFTAP == pAdapter->device_mode ||
WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->dfs_cac_block_tx = VOS_TRUE;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
return true;
}
/**---------------------------------------------------------------------------
\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
{
/* Invalid MAC found */
return 0;
}
++pMacAddr;
}
return (xdigit == 12 && (separator == 5 || separator == 0));
}
/**
* __hdd_open() - HDD Open function
* @dev: pointer to net_device structure
*
* This is called in response to ifconfig up
*
* Return: 0 for success and error number for failure
*/
static int __hdd_open(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
v_BOOL_t in_standby = TRUE;
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
pAdapter->sessionId, pAdapter->device_mode));
/* Don't validate for load/unload and logp as if we return
failure we may endup in scan/connection related issues */
if (NULL == pHddCtx || NULL == pHddCtx->cfg_ini) {
hddLog(LOG1, FL("HDD context is Null"));
return -ENODEV;
}
pHddCtx->driver_being_stopped = false;
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(LOG1, FL("chip already out of standby"));
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(LOGE, FL("Failed to bring wlan out of power save"));
return -EINVAL;
}
}
set_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) {
hddLog(LOG1, FL("Enabling Tx Queues"));
/* Enable TX queues only when we are connected */
wlan_hdd_netif_queue_control(pAdapter,
WLAN_START_ALL_NETIF_QUEUE,
WLAN_CONTROL_PATH);
}
/* Enable carrier and transmit queues for NDI */
if (WLAN_HDD_IS_NDI(pAdapter)) {
hddLog(LOG1, FL("Enabling Tx Queues"));
wlan_hdd_netif_queue_control(pAdapter,
WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
WLAN_CONTROL_PATH);
}
return 0;
}
/**
* hdd_open() - Wrapper function for __hdd_open to protect it from SSR
* @dev: pointer to net_device structure
*
* This is called in response to ifconfig up
*
* Return: 0 for success and error number for failure
*/
static int hdd_open(struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_open(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __hdd_mon_open() - HDD monitor open
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static int __hdd_mon_open(struct net_device *dev)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
int ret;
VOS_STATUS vos_status;
eHalStatus hal_status;
WLAN_STADescType sta_desc = {0};
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
adapter->sessionId, adapter->device_mode));
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
/* peer is created wma_vdev_attach->wma_create_peer */
vos_status = WLANTL_RegisterSTAClient(hdd_ctx->pvosContext,
hdd_mon_rx_packet_cbk,
&sta_desc, 0);
if (VOS_STATUS_SUCCESS != vos_status) {
hddLog(LOGE,
FL("WLANTL_RegisterSTAClient() failed to register. Status= %d [0x%08X]"),
vos_status, vos_status);
goto exit;
}
hal_status = sme_create_mon_session(hdd_ctx->hHal,
adapter->macAddressCurrent.bytes);
if (eHAL_STATUS_SUCCESS != hal_status) {
hddLog(LOGE,
FL("sme_create_mon_session() failed to register. Status= %d [0x%08X]"),
hal_status, hal_status);
goto exit;
}
return ret;
exit:
return -EIO;
}
/**
* hdd_mon_open() - SSR wrapper function for __hdd_mon_open
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static 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 wlan_hdd_monitor_mode_enable(hdd_context_t *hdd_ctx, bool enable)
{
int ret = 0;
ret = process_wma_set_command(
0,
GEN_PDEV_MONITOR_MODE,
enable,
GEN_CMD);
if (ret) {
hddLog(LOGE,
FL("send monitor enable cmd fail, ret %d"),
ret);
return ret;
}
hdd_ctx->is_mon_enable = enable;
return ret;
}
/**
* __hdd_vir_mon_open() - HDD monitor open
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static int __hdd_vir_mon_open(struct net_device *dev)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_adapter_t *sta_adapter = NULL;
int ret;
VOS_STATUS vos_status;
if (!hdd_ctx->cfg_ini->mon_on_sta_enable) {
hddLog(LOGE,
FL("monitor feature for STA is not enabled"));
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
adapter->sessionId, adapter->device_mode));
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
/* register monitor RX callback to TLSHIM and OL */
vos_status = tl_register_vir_mon_cb(hdd_ctx->pvosContext,
hdd_vir_mon_rx_cbk);
if (VOS_STATUS_SUCCESS != vos_status) {
hddLog(LOGE,
FL("failed to register monitor cbk"));
goto exit;
}
/* get STA adapter to check if STA is connected */
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if ((NULL == sta_adapter) ||
(WLAN_HDD_ADAPTER_MAGIC != sta_adapter->magic)) {
hddLog(LOGE,
FL("STA adapter is not existed."));
return ret;
}
if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter)) &&
(false == hdd_ctx->is_mon_enable)) {
/* send WMI cmd to enable monitor function */
ret = wlan_hdd_monitor_mode_enable(hdd_ctx, true);
/* disable Sta BMPS */
if (hdd_ctx->cfg_ini->enablePowersaveOffload)
sme_PsOffloadDisablePowerSave(
WLAN_HDD_GET_HAL_CTX(sta_adapter),
NULL,
NULL,
sta_adapter->sessionId);
#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled &&
hdd_ctx->cfg_ini->isRoamOffloadScanEnabled)
sme_stopRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter),
sta_adapter->sessionId, 0);
#endif
}
return ret;
exit:
return -EIO;
}
/**
* __hdd_vir_mon_stop() - HDD monitor stop
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static int __hdd_vir_mon_stop(struct net_device *dev)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_adapter_t *sta_adapter = NULL;
int ret;
VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
if (!hdd_ctx->cfg_ini->mon_on_sta_enable) {
hddLog(LOGE,
FL("monitor feature for STA is not enabled"));
goto exit;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_OPEN_REQUEST,
adapter->sessionId, adapter->device_mode));
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
/* if monitor is enabled already,
* send WMI cmd to disable target monitor.
*/
if (true == hdd_ctx->is_mon_enable) {
ret = wlan_hdd_monitor_mode_enable(hdd_ctx, false);
/* get STA adapter to check if STA is connected */
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (sta_adapter &&
(WLAN_HDD_ADAPTER_MAGIC == sta_adapter->magic) &&
hdd_connIsConnected(
WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter))) {
/* enable BMPS power save */
if (hdd_ctx->cfg_ini->enablePowersaveOffload)
sme_PsOffloadEnablePowerSave(
WLAN_HDD_GET_HAL_CTX(sta_adapter),
sta_adapter->sessionId);
#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
/* enable roaming */
if (hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled &&
hdd_ctx->cfg_ini->isRoamOffloadScanEnabled)
sme_startRoaming(WLAN_HDD_GET_HAL_CTX(sta_adapter),
sta_adapter->sessionId,
REASON_CONNECT);
#endif
}
}
/* Deregister monitor RX callback to TLSHIM and OL */
vos_status = tl_deregister_vir_mon_cb(hdd_ctx->pvosContext);
if (VOS_STATUS_SUCCESS != vos_status) {
hddLog(LOGE,
FL("failed to deregister monitor cbk"));
goto exit;
}
return ret;
exit:
return -EIO;
}
/**
* hdd_vir_mon_open()
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static int hdd_vir_mon_open(struct net_device *dev)
{
int ret = 0;
vos_ssr_protect(__func__);
__hdd_vir_mon_open(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* hdd_vir_mon_stop()
* @dev: pointer to net_device
*
* Return: 0 for success and error number for failure
*/
static int hdd_vir_mon_stop(struct net_device *dev)
{
int ret = 0;
vos_ssr_protect(__func__);
__hdd_vir_mon_stop(dev);
vos_ssr_unprotect(__func__);
return ret;
}
#ifdef MODULE
/**
* wlan_hdd_stop_enter_lowpower() - Enter low power mode
* @hdd_ctx: HDD context
*
* For module, when all the interfaces are down, enter low power mode.
*/
void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: All Interfaces are Down entering standby",
__func__);
if (VOS_STATUS_SUCCESS != wlan_hdd_enter_lowpower(hdd_ctx)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to put wlan in power save",
__func__);
}
}
/**
* wlan_hdd_stop_can_enter_lowpower() - Enter low power mode
* @adapter: Adapter context
*
* Check if hardware can enter low power mode when all the interfaces are down.
*/
static inline int wlan_hdd_stop_can_enter_lowpower(hdd_adapter_t *adapter)
{
/* SoftAP ifaces should never go in power save mode making
* sure same here.
*/
if ((WLAN_HDD_SOFTAP == adapter->device_mode) ||
(WLAN_HDD_MONITOR == adapter->device_mode) ||
(WLAN_HDD_P2P_GO == adapter->device_mode))
return 0;
return 1;
}
#else
/**
* kickstart_driver_handler() - Work queue handler for kickstart_driver
*
* Use worker queue to exit if it is not possible to call kickstart_driver()
* directly in the caller context like in interface down context
*/
static void kickstart_driver_handler(struct work_struct *work)
{
hdd_driver_exit();
wlan_hdd_inited = 0;
}
static DECLARE_WORK(kickstart_driver_work, kickstart_driver_handler);
/**
* kickstart_driver() - Initialize and Clean-up driver
* @load: True: initialize, False: Clean-up driver
* @mode_change: tell if last mode and current mode is same or not
*
* Delayed driver initialization when driver is statically linked and Clean-up
* when all the interfaces are down or any other condition which requires to
* save power by bringing down hardware.
* This routine is invoked when module parameter fwpath is modified from user
* space to signal the initialization of the WLAN driver or when all the
* interfaces are down and user space no longer need WLAN interfaces. Userspace
* needs to write to fwpath again to get the WLAN interfaces
*
* Return: 0 on success, non zero on failure
*/
static int kickstart_driver(bool load, bool mode_change)
{
int ret_status;
pr_info("%s: load: %d wlan_hdd_inited: %d, mode_change: %d caller: %pf\n",
__func__, load, wlan_hdd_inited,
mode_change, (void *)_RET_IP_);
/* Make sure unload and load are synchronized */
flush_work(&kickstart_driver_work);
/* No-Op, If unload requested even though driver is not loaded */
if (!load && !wlan_hdd_inited)
return 0;
/* Unload is requested */
if (!load && wlan_hdd_inited) {
schedule_work(&kickstart_driver_work);
return 0;
}
if (!wlan_hdd_inited) {
ret_status = hdd_driver_init();
wlan_hdd_inited = ret_status ? 0 : 1;
return ret_status;
}
if (load && wlan_hdd_inited && !mode_change) {
ret_status = 0;
} else {
hdd_driver_exit();
msleep(200);
ret_status = hdd_driver_init();
wlan_hdd_inited = ret_status ? 0 : 1;
}
return ret_status;
}
/**
* wlan_hdd_stop_enter_lowpower() - Enter low power mode
* @hdd_ctx: HDD context
*
* For static driver, when all the interfaces are down, enter low power mode by
* bringing down WLAN hardware.
*/
void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
{
bool ready;
/* Do not clean up n/w ifaces if we are in DRIVER STOP phase or else
* DRIVER START will fail and Wi-Fi will not resume successfully
*/
if (hdd_ctx && !hdd_ctx->driver_being_stopped) {
ready = vos_is_load_unload_ready(__func__);
if (!ready) {
VOS_ASSERT(0);
return;
}
vos_load_unload_protect(__func__);
kickstart_driver(false, false);
vos_load_unload_unprotect(__func__);
}
}
/**
* wlan_hdd_stop_can_enter_lowpower() - Enter low power mode
* @adapter: Adapter context
*
* Check if hardware can enter low power mode when all the interfaces are down.
* For static driver, hardware can enter low power mode for all types of
* interfaces.
*
* Return: true for power save allowed and false for power save not allowed
*/
static inline bool wlan_hdd_stop_can_enter_lowpower(hdd_adapter_t *adapter)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
/* In static driver case, we need to distinguish between WiFi OFF and
* DRIVER STOP. In both cases "ifconfig down" is happening. In OFF case,
* want to allow lowest power mode and driver cleanup. In case of DRIVER
* STOP do not want to allow power collapse for GO/SAP case. STOP
* behavior is now identical across both DLKM and Static driver case.
*/
if (hdd_ctx && !hdd_ctx->driver_being_stopped)
return true;
else if ((WLAN_HDD_SOFTAP == adapter->device_mode) ||
(WLAN_HDD_MONITOR == adapter->device_mode) ||
(WLAN_HDD_P2P_GO == adapter->device_mode))
return false;
else
return true;
}
#endif
/**
* __hdd_stop() - HDD stop function
* @dev: pointer to net_device structure
*
* This is called in response to ifconfig down
*
* Return: 0 for success and error number for failure
*/
static int __hdd_stop(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
v_BOOL_t enter_standby = TRUE;
int ret;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD, TRACE_CODE_HDD_STOP_REQUEST,
pAdapter->sessionId, pAdapter->device_mode));
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != 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;
}
/* Make sure the interface is marked as closed */
clear_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
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(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
/*
* NAN data interface is different in some sense. The traffic on NDI is
* bursty in nature and depends on the need to transfer. The service layer
* may down the interface after the usage and up again when required.
* In some sense, the NDI is expected to be available (like SAP) iface
* until NDI delete request is issued by the service layer.
* Skip BSS termination and adapter deletion for NAN Data interface (NDI).
*/
if (WLAN_HDD_IS_NDI(pAdapter))
return 0;
/* 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_FALSE);
/* DeInit the adapter. This ensures datapath cleanup as well */
hdd_deinit_adapter(pHddCtx, pAdapter, true);
/* SoftAP ifaces should never go in power save mode
making sure same here. */
if (!wlan_hdd_stop_can_enter_lowpower(pAdapter))
{
/* 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)
wlan_hdd_stop_enter_lowpower(pHddCtx);
EXIT();
return 0;
}
/**
* hdd_stop() - Wrapper function for __hdd_stop to protect it from SSR
* @dev: pointer to net_device structure
*
* This is called in response to ifconfig down
*
* Return: 0 for success and error number for failure
*/
static int hdd_stop (struct net_device *dev)
{
int ret;
vos_ssr_protect(__func__);
ret = __hdd_stop(dev);
vos_ssr_unprotect(__func__);
return ret;
}
/**
* __hdd_uninit() - HDD uninit function
* @dev: pointer to net_device structure
*
* This is called during the netdev unregister to uninitialize all data
* associated with the device
*
* Return: none
*/
static void __hdd_uninit(struct net_device *dev)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) {
hddLog(LOGP, FL("Invalid magic"));
return;
}
if (NULL == pAdapter->pHddCtx) {
hddLog(LOGP, FL("NULL pHddCtx"));
return;
}
if (dev != pAdapter->dev)
hddLog(LOGP, FL("Invalid device reference"));
hdd_deinit_adapter(pAdapter->pHddCtx, pAdapter, true);
/* After uninit our adapter structure will no longer be valid */
pAdapter->dev = NULL;
pAdapter->magic = 0;
pAdapter->pHddCtx = NULL;
EXIT();
}
/**
* hdd_uninit() - Wrapper function to protect __hdd_uninit from SSR
* @dev: pointer to net_device structure
*
* This is called during the netdev unregister to uninitialize all data
* associated with the device
*
* Return: none
*/
static void hdd_uninit(struct net_device *dev)
{
vos_ssr_protect(__func__);
__hdd_uninit(dev);
vos_ssr_unprotect(__func__);
}
/**---------------------------------------------------------------------------
\brief hdd_full_pwr_cbk() - 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 - callback context - 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);
}
}
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 || cesium_nl_srv_sock == NULL) {
hddLog(LOGE, FL("cesium process not registered, pid: %d, nl_sock: %pK"),
cesium_pid, cesium_nl_srv_sock);
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(void)
{
#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(void)
{
if (NULL != cesium_nl_srv_sock)
{
netlink_kernel_release(cesium_nl_srv_sock);
cesium_nl_srv_sock = NULL;
}
}
/**---------------------------------------------------------------------------
\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;
}
/**
* __hdd_set_mac_address() - HDD set mac address
* @dev: pointer to net_device structure
* @addr: Pointer to the sockaddr
*
* This function sets the user specified mac address using
* the command ifconfig wlanX hw ether <mac adress>.
*
* Return: 0 for success.
*/
static int __hdd_set_mac_address(struct net_device *dev, void *addr)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx;
struct sockaddr *psta_mac_addr = addr;
int ret;
ENTER();
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
memcpy(&pAdapter->macAddressCurrent, psta_mac_addr->sa_data, ETH_ALEN);
memcpy(dev->dev_addr, psta_mac_addr->sa_data, ETH_ALEN);
EXIT();
return 0;
}
/**
* hdd_set_mac_address() - Wrapper function to protect __hdd_set_mac_address()
* function from SSR
* @dev: pointer to net_device structure
* @addr: Pointer to the sockaddr
*
* This function sets the user specified mac address using
* the command ifconfig wlanX hw ether <mac adress>.
*
* Return: 0 for success.
*/
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;
}
#ifdef WLAN_FEATURE_PACKET_FILTERING
/**
* __hdd_set_multicast_list() - set the multicast address list
* @dev: pointer to net_device
*
* Return: none
*/
static void __hdd_set_multicast_list(struct net_device *dev)
{
static const uint8_t ipv6_router_solicitation[] =
{0x33, 0x33, 0x00, 0x00, 0x00, 0x02};
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int mc_count;
int i = 0;
struct netdev_hw_addr *ha;
ENTER();
if (VOS_FTM_MODE == hdd_get_conparam())
return;
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
return;
if (!pHddCtx->cfg_ini->fEnableMCAddrList) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("mc addr list ini is disabled"));
return;
}
/* Delete already configured multicast address list */
if (0 < pAdapter->mc_addr_list.mc_cnt)
if (wlan_hdd_set_mc_addr_list(pAdapter, false)) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("failed to clear mc addr list"));
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, max_mc_addr_list : %d",
__func__, mc_count, pHddCtx->max_mc_addr_list);
if (mc_count > pHddCtx->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;
}
netdev_for_each_mc_addr(ha, dev) {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("ha_addr[%d] "MAC_ADDRESS_STR),
i, MAC_ADDR_ARRAY(ha->addr));
if (i == mc_count)
break;
/*
* Skip following addresses:
* 1)IPv6 router solicitation address
* 2)Any other address pattern if its set during RXFILTER REMOVE
* driver command based on addr_filter_pattern
*/
if ((!memcmp(ha->addr, ipv6_router_solicitation, ETH_ALEN)) ||
(pAdapter->addr_filter_pattern && (!memcmp(ha->addr,
&pAdapter->addr_filter_pattern, 1)))) {
hddLog(LOG1, FL("MC/BC filtering Skip addr ="MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(ha->addr));
continue;
}
memset(&(pAdapter->mc_addr_list.addr[i * ETH_ALEN]), 0, ETH_ALEN);
memcpy(&(pAdapter->mc_addr_list.addr[i * ETH_ALEN]),
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 * ETH_ALEN]));
pAdapter->mc_addr_list.mc_cnt++;
i++;
}
}
/* Configure the updated multicast address list */
if (wlan_hdd_set_mc_addr_list(pAdapter, true))
hddLog(VOS_TRACE_LEVEL_INFO, FL("failed to set mc addr list"));
EXIT();
return;
}
/**
* hdd_set_multicast_list() - SSR wrapper function for __hdd_set_multicast_list
* @dev: pointer to net_device
*
* Return: none
*/
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
--------------------------------------------------------------------------*/
static 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);
}
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)) || defined(WITH_BACKPORTS)
.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_stop,
#ifdef CONFIG_HL_SUPPORT
.ndo_start_xmit = hdd_hard_start_xmit,
#endif
.ndo_get_stats = hdd_stats,
};
static struct net_device_ops wlan_mon_dev_ops = {
.ndo_open = hdd_vir_mon_open,
.ndo_stop = hdd_vir_mon_stop,
};
void hdd_set_station_ops( struct net_device *pWlanDev )
{
if (VOS_MONITOR_MODE == hdd_get_conparam())
pWlanDev->netdev_ops = &wlan_mon_drv_ops;
else
pWlanDev->netdev_ops = &wlan_drv_ops;
}
void hdd_set_monitor_ops(struct net_device *pwlan_dev)
{
pwlan_dev->netdev_ops = &wlan_mon_dev_ops;
}
static void mon_mode_ether_setup(struct net_device *dev)
{
dev->header_ops = NULL;
dev->type = ARPHRD_IEEE80211_RADIOTAP;
dev->hard_header_len = ETH_HLEN;
dev->mtu = ETH_DATA_LEN;
dev->addr_len = ETH_ALEN;
dev->tx_queue_len = 1000; /* Ethernet wants good queues */
dev->flags = IFF_BROADCAST|IFF_MULTICAST;
dev->priv_flags |= IFF_TX_SKB_SHARING;
memset(dev->broadcast, 0xFF, ETH_ALEN);
}
#ifdef FEATURE_RUNTIME_PM
/**
* hdd_runtime_suspend_init() - API to initialize runtime pm context
* @hdd_ctx: HDD Context
*
* The API initializes the context to prevent runtime pm for various use
* cases like scan, roc, dfs.
* This API can be extended to initialize the context to prevent runtime pm
*
* Return: void
*/
void hdd_runtime_suspend_init(hdd_context_t *hdd_ctx)
{
struct hdd_runtime_pm_context *context = &hdd_ctx->runtime_context;
context->scan = vos_runtime_pm_prevent_suspend_init("scan");
context->roc = vos_runtime_pm_prevent_suspend_init("roc");
context->dfs = vos_runtime_pm_prevent_suspend_init("dfs");
context->obss = vos_runtime_pm_prevent_suspend_init("obss");
}
/**
* hdd_runtime_suspend_deinit() - API to deinit runtime pm context
* @hdd_ctx: HDD context
*
* The API deinit the context to prevent runtime pm.
*
* Return: void
*/
void hdd_runtime_suspend_deinit(hdd_context_t *hdd_ctx)
{
struct hdd_runtime_pm_context *context = &hdd_ctx->runtime_context;
vos_runtime_pm_prevent_suspend_deinit(context->scan);
context->scan = NULL;
vos_runtime_pm_prevent_suspend_deinit(context->roc);
context->roc = NULL;
vos_runtime_pm_prevent_suspend_deinit(context->dfs);
context->dfs = NULL;
vos_runtime_pm_prevent_suspend_deinit(context->obss);
context->obss = NULL;
}
/**
* hdd_adapter_runtime_suspend_init() - API to init runtime pm context/adapter
* @adapter: Interface Adapter
*
* API is used to init the context to prevent runtime pm/adapter
*
* Return: void
*/
static void
hdd_adapter_runtime_suspend_init(hdd_adapter_t *adapter)
{
struct hdd_adapter_pm_context *context = &adapter->runtime_context;
context->connect = vos_runtime_pm_prevent_suspend_init("connect");
}
/**
* hdd_adapter_runtime_suspend_denit() - API to deinit runtime pm/adapter
* @adapter: Interface Adapter
*
* API is used to deinit the context to prevent runtime pm/adapter
*
* Return: void
*/
static void hdd_adapter_runtime_suspend_denit(hdd_adapter_t *adapter)
{
struct hdd_adapter_pm_context *context = &adapter->runtime_context;
vos_runtime_pm_prevent_suspend_deinit(context->connect);
context->connect = NULL;
}
#else
void hdd_runtime_suspend_init(hdd_context_t *hdd_ctx) { }
void hdd_runtime_suspend_deinit(hdd_context_t *hdd_ctx) { }
static inline void
hdd_adapter_runtime_suspend_init(hdd_adapter_t *adapter) { }
static inline void
hdd_adapter_runtime_suspend_denit(hdd_adapter_t *adapter) { }
#endif
/**
* hdd_adapter_init_action_frame_random_mac() - Initialze attributes needed for
* randomization of SA in management action frames
* @adapter: Pointer to adapter
*
* Return: None
*/
static void hdd_adapter_init_action_frame_random_mac(hdd_adapter_t *adapter)
{
spin_lock_init(&adapter->random_mac_lock);
vos_mem_zero(adapter->random_mac, sizeof(adapter->random_mac));
}
static hdd_adapter_t* hdd_alloc_station_adapter(hdd_context_t *pHddCtx,
tSirMacAddr macAddr,
unsigned char name_assign_type,
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)) || defined(WITH_BACKPORTS)
name_assign_type,
#endif
(VOS_MONITOR_MODE == vos_get_conparam()?
mon_mode_ether_setup : 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;
vos_event_init(&pAdapter->scan_info.scan_finished_event);
pAdapter->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP;
pAdapter->offloads_configured = FALSE;
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;
/*
* kernel will consume ethernet header length buffer for hard_header,
* so just reserve it
*/
hdd_set_needed_headroom(pWlanDev, pWlanDev->hard_header_len);
if (pHddCtx->cfg_ini->enableIPChecksumOffload)
pWlanDev->features |= NETIF_F_HW_CSUM;
else if (pHddCtx->cfg_ini->enableTCPChkSumOffld)
pWlanDev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
pWlanDev->features |= NETIF_F_RXCSUM;
hdd_set_station_ops( pAdapter->dev );
pWlanDev->priv_destructor = free_netdev;
pWlanDev->ieee80211_ptr = &pAdapter->wdev ;
pWlanDev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN;
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 );
hdd_adapter_runtime_suspend_init(pAdapter);
spin_lock_init(&pAdapter->pause_map_lock);
pAdapter->last_tx_jiffies = jiffies;
pAdapter->bug_report_count = 0;
pAdapter->start_time = pAdapter->last_time = vos_system_ticks();
}
return pAdapter;
}
static hdd_adapter_t *hdd_alloc_monitor_adapter(hdd_context_t *pHddCtx,
tSirMacAddr macAddr,
unsigned char name_assign_type,
const char *name)
{
struct net_device *pwlan_dev = NULL;
hdd_adapter_t *pAdapter = NULL;
/*
* cfg80211 initialization and registration....
*/
pwlan_dev = alloc_netdev_mq(sizeof(hdd_adapter_t),
name,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) || defined(WITH_BACKPORTS)
name_assign_type,
#endif
mon_mode_ether_setup,
NUM_TX_QUEUES);
if (pwlan_dev != NULL) {
/* Save the pointer to the net_device in the HDD adapter */
pAdapter = (hdd_adapter_t *)netdev_priv(pwlan_dev);
vos_mem_zero(pAdapter, sizeof(hdd_adapter_t));
pAdapter->dev = pwlan_dev;
pAdapter->pHddCtx = pHddCtx;
pAdapter->magic = WLAN_HDD_ADAPTER_MAGIC;
vos_event_init(&pAdapter->scan_info.scan_finished_event);
pAdapter->scan_info.scan_pending_option = WEXT_SCAN_PENDING_GIVEUP;
pAdapter->offloads_configured = FALSE;
pAdapter->isLinkUpSvcNeeded = FALSE;
pAdapter->higherDtimTransition = eANI_BOOLEAN_TRUE;
/* Init the net_device structure */
strlcpy(pwlan_dev->name, name, IFNAMSIZ);
vos_mem_copy(pwlan_dev->dev_addr,
(void *)macAddr, sizeof(tSirMacAddr));
vos_mem_copy(pAdapter->macAddressCurrent.bytes,
macAddr, sizeof(tSirMacAddr));
pwlan_dev->watchdog_timeo = HDD_TX_TIMEOUT;
/*
* kernel will consume ethernet header length buffer for hard_header,
* so just reserve it
*/
hdd_set_needed_headroom(pwlan_dev, pwlan_dev->hard_header_len);
if (pHddCtx->cfg_ini->enableIPChecksumOffload)
pwlan_dev->features |= NETIF_F_HW_CSUM;
else if (pHddCtx->cfg_ini->enableTCPChkSumOffld)
pwlan_dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
pwlan_dev->features |= NETIF_F_RXCSUM;
hdd_set_monitor_ops(pAdapter->dev);
pwlan_dev->priv_destructor = free_netdev;
pwlan_dev->ieee80211_ptr = &pAdapter->wdev;
pwlan_dev->tx_queue_len = HDD_NETDEV_TX_QUEUE_LEN;
pAdapter->wdev.wiphy = pHddCtx->wiphy;
pAdapter->wdev.netdev = pwlan_dev;
/* set pwlan_dev's parent to underlying device */
SET_NETDEV_DEV(pwlan_dev, pHddCtx->parent_dev);
hdd_wmm_init(pAdapter);
hdd_adapter_runtime_suspend_init(pAdapter);
spin_lock_init(&pAdapter->pause_map_lock);
pAdapter->last_tx_jiffies = jiffies;
pAdapter->bug_report_count = 0;
pAdapter->start_time = pAdapter->last_time = vos_system_ticks();
}
return pAdapter;
}
VOS_STATUS hdd_register_interface( hdd_adapter_t *pAdapter, tANI_U8 rtnl_lock_held )
{
struct net_device *pWlanDev = pAdapter->dev;
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;
}
/**
* hdd_smeCloseSessionCallback() - HDD callback function
* @pContext: adapter context
*
* This is a callback function HDD registers with SME.
*
* Returns: eHalstatus
*/
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;
}
/*
* For NAN Data interface, the close session results in the final
* indication to the userspace
*/
hdd_ndp_session_end_handler(pAdapter);
clear_bit(SME_SESSION_OPENED, &pAdapter->event_flags);
#if !defined (CONFIG_CNSS) && \
!defined (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 work queue 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 signalling 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) {
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
}
status = hdd_get_next_adapter(hdd_ctx, adapter_node,
&next_adapter);
adapter_node = next_adapter;
}
EXIT();
}
/**
* hdd_check_and_init_tdls() - check and init TDLS operation for desired mode
* @adapter: pointer to device adapter
* @type: type of interface
*
* This routine will check the mode of adapter and if it is required then it
* will initialize the TDLS operations
*
* Return: VOS_STATUS
*/
#ifdef FEATURE_WLAN_TDLS
static VOS_STATUS hdd_check_and_init_tdls(hdd_adapter_t *adapter, uint32_t type)
{
if (VOS_IBSS_MODE != type) {
if (0 != wlan_hdd_tdls_init(adapter)) {
hddLog(LOGE, FL("wlan_hdd_tdls_init failed"));
return VOS_STATUS_E_FAILURE;
}
set_bit(TDLS_INIT_DONE, &adapter->event_flags);
}
return VOS_STATUS_SUCCESS;
}
#else
static VOS_STATUS hdd_check_and_init_tdls(hdd_adapter_t *adapter, uint32_t type)
{
return VOS_STATUS_SUCCESS;
}
#endif
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;
tANI_U32 type, subType;
unsigned long rc;
int ret_val;
INIT_COMPLETION(pAdapter->session_open_comp_var);
sme_SetCurrDeviceMode(pHddCtx->hHal, pAdapter->device_mode);
sme_set_pdev_ht_vht_ies(pHddCtx->hHal, pHddCtx->cfg_ini->enable2x2);
status = vos_get_vdev_types(pAdapter->device_mode, &type, &subType);
if (VOS_STATUS_SUCCESS != status)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "failed to get vdev type");
goto error_sme_open;
}
ret_val = process_wma_set_command((int)pAdapter->sessionId,
(int)WMI_PDEV_PARAM_BURST_ENABLE,
(int)pHddCtx->cfg_ini->enableSifsBurst,
PDEV_CMD);
if (0 != ret_val) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_BURST_ENABLE set failed %d",
__func__, ret_val);
}
//Open a SME session for future operation
halStatus = sme_OpenSession( pHddCtx->hHal, hdd_smeRoamCallback, pAdapter,
(tANI_U8 *)&pAdapter->macAddressCurrent, &pAdapter->sessionId,
type, subType);
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) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("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;
}
//Set the Connection State to Not Connected
hdd_connSetConnectionState(pAdapter, 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);
status = hdd_check_and_init_tdls(pAdapter, type);
if (status != VOS_STATUS_SUCCESS)
goto error_tdls_init;
return VOS_STATUS_SUCCESS;
error_tdls_init:
clear_bit(WMM_INIT_DONE, &pAdapter->event_flags);
hdd_wmm_adapter_close(pAdapter);
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,
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 )
{
unsigned long rc;
INIT_COMPLETION(pAdapter->tx_action_cnf_event);
rc = wait_for_completion_timeout(
&pAdapter->tx_action_cnf_event,
msecs_to_jiffies(ACTION_FRAME_TX_TIMEOUT));
if (!rc)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s HDD Wait for Action Confirmation Failed!!",
__func__);
/* 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,
bool rtnl_held)
{
ENTER();
switch ( pAdapter->device_mode )
{
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);
wlan_hdd_tdls_exit(pAdapter);
break;
}
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_GO:
{
if (test_bit(INIT_TX_RX_SUCCESS, &pAdapter->event_flags))
{
hdd_softap_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);
}
wlan_hdd_undo_acs(pAdapter);
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;
}
default:
break;
}
EXIT();
}
void hdd_cleanup_adapter(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter,
tANI_U8 rtnl_held)
{
struct net_device *pWlanDev = NULL;
if (pAdapter)
pWlanDev = pAdapter->dev;
else {
hddLog(LOGE, FL("pAdapter is Null"));
return;
}
hdd_adapter_runtime_suspend_denit(pAdapter);
/* The adapter is marked as closed. When hdd_wlan_exit() call returns,
* the driver is almost closed and cannot handle either control
* messages or data. However, unregister_netdevice() call above will
* eventually invoke hdd_stop (ndo_close) driver callback, which attempts
* to close the active connections (basically excites control path) which
* is not right. Setting this flag helps hdd_stop() to recognize that
* the interface is closed and restricts any operations on that
*/
clear_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
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 */
}
}
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", 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 (0 != wlan_hdd_validate_context(pHddCtx))
return VOS_STATUS_E_PERM;
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)
{
unsigned long rc;
//Block on a completion variable. Can't wait forever though
rc = wait_for_completion_timeout(
&pHddCtx->full_pwr_comp_var,
msecs_to_jiffies(1000));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: wait on full_pwr_comp_var failed",
__func__);
}
}
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;
}
VOS_STATUS hdd_check_for_existing_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 VOS_STATUS_E_FAILURE;
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
return VOS_STATUS_SUCCESS;
}
uint32_t hdd_get_current_vdev_sta_count(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *hdd_adapter_node, *next;
VOS_STATUS status;
uint32_t vdev_sta_cnt = 0;
status = hdd_get_front_adapter(hdd_ctx, &hdd_adapter_node);
while (NULL != hdd_adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = hdd_adapter_node->pAdapter;
if ((NULL != adapter) &&
((WLAN_HDD_INFRA_STATION == adapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == adapter->device_mode) ||
(WLAN_HDD_IBSS == adapter->device_mode)))
vdev_sta_cnt++;
status = hdd_get_next_adapter(hdd_ctx, hdd_adapter_node, &next);
hdd_adapter_node = next;
}
return vdev_sta_cnt;
}
hdd_adapter_t *hdd_open_adapter(hdd_context_t *hdd_ctx,
uint8_t session_type,
const char *iface_name,
tSirMacAddr mac_addr,
uint8_t name_assign_type,
uint8_t rtnl_held)
{
VOS_STATUS status, exit_bmps_status = VOS_STATUS_E_FAILURE;
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *hdd_adapter_node;
hdd_cfg80211_state_t *cfg_state;
int ret;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: iface =%s type = %d\n",
__func__, iface_name, session_type);
if (hdd_ctx->current_intf_count >= hdd_ctx->max_intf_count) {
/*
* Max limit reached on the number of vdevs
* configured by the host.
*/
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unable to add virtual intf: "
"current_vdev_cnt=%d,host_configured_vdev_cnt=%d",
__func__,
hdd_ctx->current_intf_count,
hdd_ctx->max_intf_count);
return NULL;
}
if (((WLAN_HDD_INFRA_STATION == session_type) ||
(WLAN_HDD_P2P_CLIENT == session_type) ||
(WLAN_HDD_IBSS == session_type)) &&
(WLAN_HDD_VDEV_STA_MAX ==
hdd_get_current_vdev_sta_count(hdd_ctx))) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unable to add sta interface: max sta cnt is %d",
__func__, WLAN_HDD_VDEV_STA_MAX);
return NULL;
}
if (hdd_cfg_is_sub20_channel_width_enabled(hdd_ctx) &&
(hdd_ctx->current_intf_count >= 1)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Unable add another virtual intf when sub20 enable",
__func__);
return NULL;
}
if (NULL == mac_addr) {
/* Not received valid macAddr */
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s:Unable to add virtual intf: Not able to get"
"valid mac address", __func__);
return NULL;
}
status = hdd_check_for_existing_macaddr(hdd_ctx, mac_addr);
if (VOS_STATUS_E_FAILURE == status) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Duplicate MAC addr: "MAC_ADDRESS_STR
" already exists",
__func__, MAC_ADDR_ARRAY(mac_addr));
return NULL;
}
/*
* If Powersave Offload is enabled
* Fw will take care incase of concurrency
*/
if (!hdd_ctx->cfg_ini->enablePowersaveOffload) {
/* Disable BMPS incase of Concurrency */
exit_bmps_status = hdd_disable_bmps_imps(hdd_ctx, session_type);
if (VOS_STATUS_E_FAILURE == exit_bmps_status) {
/* 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:
/* Reset locally administered bit if the device mode is STA */
WLAN_HDD_RESET_LOCALLY_ADMINISTERED_BIT(mac_addr);
/* fall through */
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
case WLAN_HDD_OCB:
case WLAN_HDD_NDI:
{
adapter = hdd_alloc_station_adapter(hdd_ctx,
mac_addr,
name_assign_type,
iface_name);
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("%s: Failed to allocate adapter for session %d"),
__func__, session_type);
return NULL;
}
if (0 != hdd_init_packet_filtering(hdd_ctx, adapter))
goto err_init_packet_filtering;
if (session_type == WLAN_HDD_P2P_CLIENT)
adapter->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT;
else if (VOS_MONITOR_MODE == vos_get_conparam())
adapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
else
adapter->wdev.iftype = NL80211_IFTYPE_STATION;
adapter->device_mode = session_type;
hdd_initialize_adapter_common(adapter);
if (WLAN_HDD_NDI == session_type)
status = hdd_init_nan_data_mode(adapter);
else
status = hdd_init_station_mode(adapter);
if (VOS_STATUS_SUCCESS != status)
goto err_init_adapter_mode;
/* initialize action frame random mac info */
hdd_adapter_init_action_frame_random_mac(adapter);
/* Workqueue which gets scheduled in IPv4
* notification callback
*/
vos_init_work(&adapter->ipv4NotifierWorkQueue,
hdd_ipv4_notifier_work_queue);
#ifdef WLAN_NS_OFFLOAD
/* Workqueue which gets scheduled in IPv6
* notification callback
*/
vos_init_work(&adapter->ipv6NotifierWorkQueue,
hdd_ipv6_notifier_work_queue);
#endif
status = hdd_register_interface(adapter, rtnl_held);
if (VOS_STATUS_SUCCESS != status)
goto err_register_interface;
/* do not disable tx in monitor mode */
if (VOS_MONITOR_MODE != vos_get_conparam()) {
/* Stop the Interface TX queue */
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
}
#ifdef QCA_LL_TX_FLOW_CT
/* SAT mode default TX Flow control instance
* This instance will be used for
* STA mode, IBSS mode and TDLS mode */
if (adapter->tx_flow_timer_initialized == VOS_FALSE) {
vos_timer_init(&adapter->tx_flow_control_timer,
VOS_TIMER_TYPE_SW,
hdd_tx_resume_timer_expired_handler,
adapter);
adapter->tx_flow_timer_initialized = VOS_TRUE;
}
WLANTL_RegisterTXFlowControl(hdd_ctx->pvosContext,
hdd_tx_resume_cb,
adapter->sessionId,
(void *)adapter);
#endif /* QCA_LL_TX_FLOW_CT */
break;
}
case WLAN_HDD_P2P_GO:
case WLAN_HDD_SOFTAP:
{
adapter = hdd_wlan_create_ap_dev(hdd_ctx,
mac_addr,
name_assign_type,
(tANI_U8 *)iface_name);
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"),
session_type);
return NULL;
}
if (0 != hdd_init_packet_filtering(hdd_ctx, adapter))
goto err_init_packet_filtering;
adapter->wdev.iftype = (session_type == WLAN_HDD_SOFTAP) ?
NL80211_IFTYPE_AP :
NL80211_IFTYPE_P2P_GO;
adapter->device_mode = session_type;
hdd_initialize_adapter_common(adapter);
status = hdd_init_ap_mode(adapter, false);
if (VOS_STATUS_SUCCESS != status)
goto err_init_adapter_mode;
status = hdd_register_hostapd(adapter, rtnl_held);
if (VOS_STATUS_SUCCESS != status)
goto err_register_interface;
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
hdd_set_conparam(1);
/* Workqueue which gets scheduled in IPv4
* notification callback
*/
vos_init_work(&adapter->ipv4NotifierWorkQueue,
hdd_ipv4_notifier_work_queue);
#ifdef WLAN_NS_OFFLOAD
/* Workqueue which gets scheduled in IPv6
* notification callback
*/
vos_init_work(&adapter->ipv6NotifierWorkQueue,
hdd_ipv6_notifier_work_queue);
#endif
break;
}
case WLAN_HDD_FTM:
{
adapter = hdd_alloc_station_adapter(hdd_ctx,
mac_addr,
name_assign_type,
iface_name);
if (NULL == adapter) {
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.
*/
adapter->wdev.iftype = NL80211_IFTYPE_STATION;
adapter->device_mode = session_type;
status = hdd_register_interface(adapter, rtnl_held);
if (VOS_STATUS_SUCCESS != status)
goto err_register_interface;
hdd_initialize_adapter_common(adapter);
hdd_init_tx_rx(adapter);
/* Stop the Interface TX queue */
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
break;
}
case WLAN_HDD_MONITOR:
{
adapter = hdd_alloc_monitor_adapter(hdd_ctx,
mac_addr,
name_assign_type,
iface_name);
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("failed to allocate adapter for session %d"),
session_type);
return NULL;
}
adapter->wdev.iftype = NL80211_IFTYPE_MONITOR;
adapter->device_mode = session_type;
status = hdd_register_interface(adapter, rtnl_held);
if (VOS_STATUS_SUCCESS != status)
goto err_register_interface;
hdd_initialize_adapter_common(adapter);
/* Stop the Interface TX queue */
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
break;
}
default:
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s Invalid session type %d",
__func__, session_type);
VOS_ASSERT(0);
return NULL;
}
}
vos_init_work(&adapter->scan_block_work,
wlan_hdd_cfg80211_scan_block_cb);
cfg_state = WLAN_HDD_GET_CFG_STATE_PTR(adapter);
mutex_init(&cfg_state->remain_on_chan_ctx_lock);
#ifdef WLAN_FEATURE_MBSSID
hdd_mbssid_apply_def_cfg_ini(adapter);
#endif
/* Add it to the hdd's session list */
hdd_adapter_node = vos_mem_malloc(sizeof(hdd_adapter_list_node_t));
if (NULL == hdd_adapter_node)
goto err_malloc_adapter_node;
hdd_adapter_node->pAdapter = adapter;
status = hdd_add_adapter_back(hdd_ctx,
hdd_adapter_node);
if (VOS_STATUS_SUCCESS != status)
goto err_add_adapter_back;
wlan_hdd_set_concurrency_mode(hdd_ctx, session_type);
/* Initialize the WoWL service */
if (!hdd_init_wowl(adapter)) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: hdd_init_wowl failed", __func__);
goto err_post_add_adapter;
}
/* Adapter successfully added. Increment the vdev count */
hdd_ctx->current_intf_count++;
hddLog(VOS_TRACE_LEVEL_DEBUG,
"%s: current_intf_count=%d", __func__,
hdd_ctx->current_intf_count);
#ifdef FEATURE_WLAN_STA_AP_MODE_DFS_DISABLE
if (vos_get_concurrency_mode() == VOS_STA_SAP) {
hdd_adapter_t *ap_adapter;
ap_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP);
if (ap_adapter != NULL &&
test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags) &&
VOS_IS_DFS_CH(ap_adapter->sessionCtx.ap.operatingChannel)) {
hddLog(VOS_TRACE_LEVEL_WARN,
"STA-AP Mode DFS not supported. "
"Restart SAP with Non DFS ACS");
ap_adapter->sessionCtx.ap.sapConfig.channel =
AUTO_CHANNEL_SELECT;
ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.acs_mode =
true;
wlan_hdd_restart_sap(ap_adapter);
}
}
#endif
if ((vos_get_conparam() != VOS_FTM_MODE) &&
(!hdd_ctx->cfg_ini->enable2x2)) {
#define HDD_DTIM_1CHAIN_RX_ID 0x5
#define HDD_SMPS_PARAM_VALUE_S 29
/* Disable DTIM 1 chain Rx when in 1x1,
* we are passing two values as
* param_id << 29 | param_value.
* Below param_value = 0(disable)
*/
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_STA_SMPS_PARAM_CMDID,
HDD_DTIM_1CHAIN_RX_ID << HDD_SMPS_PARAM_VALUE_S,
VDEV_CMD);
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: DTIM 1 chain set failed %d",
__func__, ret);
goto err_post_add_adapter;
}
if (!hdd_ctx->per_band_chainmask_supp) {
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_PDEV_PARAM_TX_CHAIN_MASK,
(int)hdd_ctx->cfg_ini->txchainmask1x1,
PDEV_CMD);
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: PDEV_PARAM_TX_CHAIN_MASK set failed %d",
__func__, ret);
goto err_post_add_adapter;
}
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_PDEV_PARAM_RX_CHAIN_MASK,
(int)hdd_ctx->cfg_ini->rxchainmask1x1,
PDEV_CMD);
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_RX_CHAIN_MASK set failed %d",
__func__, ret);
goto err_post_add_adapter;
}
}
#undef HDD_DTIM_1CHAIN_RX_ID
#undef HDD_SMPS_PARAM_VALUE_S
}
if (VOS_FTM_MODE != vos_get_conparam()) {
uint32_t cca_threshold;
cca_threshold = hdd_ctx->cfg_ini->cca_threshold_2g |
hdd_ctx->cfg_ini->cca_threshold_5g << 8;
if (hdd_ctx->cfg_ini->cca_threshold_enable) {
hddLog(VOS_TRACE_LEVEL_DEBUG,
"%s: CCA Threshold is enabled.", __func__);
ret = process_wma_set_command((int)adapter->sessionId,
WMI_PDEV_PARAM_CCA_THRESHOLD,
cca_threshold,
PDEV_CMD);
} else {
ret = 0;
}
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_CCA_THRESHOLD set failed %d",
__func__, ret);
goto err_post_add_adapter;
}
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_PDEV_PARAM_HYST_EN,
(int)hdd_ctx->cfg_ini->enableMemDeepSleep,
PDEV_CMD);
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_HYST_EN set failed %d",
__func__, ret);
goto err_post_add_adapter;
}
hddLog(LOG1, FL("SET AMSDU num %d"),
hdd_ctx->cfg_ini->max_amsdu_num);
ret = process_wma_set_command((int)adapter->sessionId,
(int)GEN_VDEV_PARAM_AMSDU,
(int)hdd_ctx->cfg_ini->max_amsdu_num,
GEN_CMD);
if (ret != 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("GEN_VDEV_PARAM_AMSDU set failed %d"), ret);
goto err_post_add_adapter;
}
}
#ifdef CONFIG_FW_LOGS_BASED_ON_INI
/* Enable FW logs based on INI configuration */
if ((VOS_FTM_MODE != vos_get_conparam()) &&
(hdd_ctx->cfg_ini->enablefwlog) &&
(hdd_ctx->current_intf_count == 1)) {
uint8_t count = 0;
uint32_t value = 0;
uint8_t num_entries = 0;
uint8_t module_loglevel[FW_MODULE_LOG_LEVEL_STRING_LENGTH];
hdd_ctx->fw_log_settings.dl_type =
hdd_ctx->cfg_ini->enableFwLogType;
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_DBGLOG_TYPE,
hdd_ctx->cfg_ini->enableFwLogType,
DBG_CMD);
if (ret != 0)
hddLog(LOGE,
FL("Failed to enable FW log type ret %d"), ret);
hdd_ctx->fw_log_settings.dl_loglevel =
hdd_ctx->cfg_ini->enableFwLogLevel;
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_DBGLOG_LOG_LEVEL,
hdd_ctx->cfg_ini->enableFwLogLevel,
DBG_CMD);
if (ret != 0)
hddLog(LOGE,
FL("Failed to enable FW log level ret %d"),
ret);
hdd_string_to_u8_array(hdd_ctx->cfg_ini->enableFwModuleLogLevel,
module_loglevel,
&num_entries,
FW_MODULE_LOG_LEVEL_STRING_LENGTH);
while (count < num_entries) {
/* FW module log level input string looks like below:
* gFwDebugModuleLoglevel=
* <FW Module ID>, <Log Level>, so on....
* For example:
* gFwDebugModuleLoglevel=
* 1,0,2,1,3,2,4,3,5,4,6,5,7,6,8,7
* Above input string means :
* For FW module ID 1 enable log level 0
* For FW module ID 2 enable log level 1
* For FW module ID 3 enable log level 2
* For FW module ID 4 enable log level 3
* For FW module ID 5 enable log level 4
* For FW module ID 6 enable log level 5
* For FW module ID 7 enable log level 6
* For FW module ID 8 enable log level 7
*
* FW expects WMI command value =
* Module ID * 10 + Module Log level
*/
value = ((module_loglevel[count] * 10) +
module_loglevel[count + 1]);
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_DBGLOG_MOD_LOG_LEVEL,
value, DBG_CMD);
if (ret != 0)
hddLog(LOGE, FL("Failed to enable FW module log level %d ret %d"),
value, ret);
count += 2;
}
}
#endif
ret = process_wma_set_command((int)adapter->sessionId,
(int)WMI_VDEV_PARAM_ENABLE_RTSCTS,
hdd_ctx->cfg_ini->rts_profile, VDEV_CMD);
if (ret != 0)
hddLog(LOGE, "FAILED TO SET RTSCTS Profile ret:%d", ret);
return adapter;
err_post_add_adapter:
hdd_remove_adapter(hdd_ctx, hdd_adapter_node);
err_add_adapter_back:
vos_mem_free(hdd_adapter_node);
err_malloc_adapter_node:
if (rtnl_held)
unregister_netdevice(adapter->dev);
else
unregister_netdev(adapter->dev);
err_register_interface:
/* close sme session to detach vdev */
hdd_stop_adapter(hdd_ctx, adapter, VOS_TRUE);
hdd_deinit_adapter(hdd_ctx, adapter, rtnl_held);
err_init_adapter_mode:
hdd_deinit_packet_filtering(adapter);
err_init_packet_filtering:
hdd_adapter_runtime_suspend_denit(adapter);
free_netdev(adapter->dev);
wlan_hdd_release_intf_addr(hdd_ctx,
adapter->macAddressCurrent.bytes);
/* If bmps disabled enable it */
if (!hdd_ctx->cfg_ini->enablePowersaveOffload) {
if (VOS_STATUS_SUCCESS == exit_bmps_status) {
if (hdd_ctx->hdd_wlan_suspended)
hdd_set_pwrparams(hdd_ctx);
hdd_enable_bmps_imps(hdd_ctx);
}
}
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_deinit_packet_filtering(pAdapterNode->pAdapter);
hdd_cleanup_adapter( pHddCtx, pAdapterNode->pAdapter, rtnl_held );
hdd_remove_adapter( pHddCtx, pAdapterNode );
vos_mem_free( pAdapterNode );
pAdapterNode = NULL;
/* Adapter removed. Decrement vdev count */
if (pHddCtx->current_intf_count != 0)
pHddCtx->current_intf_count--;
/*
* If Powersave Offload is enabled,
* Fw will take care incase of concurrency
*/
if(pHddCtx->cfg_ini->enablePowersaveOffload)
return VOS_STATUS_SUCCESS;
/* 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_deinit_packet_filtering(pHddAdapterNode->pAdapter);
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)
{
tANI_U8 *bssid = NULL;
tSirUpdateIE updateIE;
switch (pHostapdAdapter->device_mode)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
{
hdd_station_ctx_t * pHddStaCtx =
WLAN_HDD_GET_STATION_CTX_PTR(pHostapdAdapter);
bssid = (tANI_U8*)&pHddStaCtx->conn_info.bssId;
break;
}
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_GO:
case WLAN_HDD_IBSS:
{
bssid = pHostapdAdapter->macAddressCurrent.bytes;
break;
}
case WLAN_HDD_MONITOR:
case WLAN_HDD_FTM:
case WLAN_HDD_P2P_DEVICE:
default:
/*
* wlan_hdd_reset_prob_rspies should not have been called
* for these kind of devices
*/
hddLog(LOGE, FL("Unexpected request for the current device type %d"),
pHostapdAdapter->device_mode);
return;
}
vos_mem_copy(updateIE.bssid, bssid, sizeof(tSirMacAddr));
updateIE.smeSessionId = pHostapdAdapter->sessionId;
updateIE.ieBufferlength = 0;
updateIE.pAdditionIEBuffer = NULL;
updateIE.append = VOS_TRUE;
updateIE.notify = VOS_FALSE;
if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pHostapdAdapter),
&updateIE, eUPDATE_IE_PROBE_RESP) == eHAL_STATUS_FAILURE) {
hddLog(LOGE, FL("Could not pass on PROBE_RSP_BCN data to PE"));
}
}
/**
* hdd_wait_for_sme_close_sesion() - Close and wait for SME session close
* @hdd_ctx: HDD context which is already NULL validated
* @adapter: HDD adapter which is already NULL validated
*
* Close the SME session and wait for its completion, if needed.
*
* Return: None
*/
static void hdd_wait_for_sme_close_sesion(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
unsigned long rc;
if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) {
hddLog(LOGE, FL("session is not opened:%d"), adapter->sessionId);
return;
}
INIT_COMPLETION(adapter->session_close_comp_var);
if (eHAL_STATUS_SUCCESS ==
sme_CloseSession(hdd_ctx->hHal, adapter->sessionId,
hdd_smeCloseSessionCallback,
adapter)) {
/*
* Block on a completion variable. Can't wait
* forever though.
*/
rc = wait_for_completion_timeout(
&adapter->session_close_comp_var,
msecs_to_jiffies
(WLAN_WAIT_TIME_SESSIONOPENCLOSE));
if (!rc)
hddLog(LOGE, FL("failure waiting for session_close_comp_var"));
}
}
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;
tSirUpdateIE updateIE ;
unsigned long rc;
ENTER();
pScanInfo = &pAdapter->scan_info;
hddLog(LOG1, FL("Disabling queues"));
wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
switch(pAdapter->device_mode)
{
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_IBSS:
case WLAN_HDD_P2P_CLIENT:
case WLAN_HDD_P2P_DEVICE:
case WLAN_HDD_NDI:
if ((WLAN_HDD_NDI == pAdapter->device_mode) ||
hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)) ||
hdd_is_connecting(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) {
INIT_COMPLETION(pAdapter->disconnect_comp_var);
/*
* For NDI do not use pWextState from sta_ctx, if needed
* extract from ndi_ctx.
*/
if (WLAN_HDD_NDI == pAdapter->device_mode)
halStatus = sme_RoamDisconnect(pHddCtx->hHal,
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_NDI_DELETE);
else if (pWextState->roamProfile.BSSType == eCSR_BSS_TYPE_START_IBSS)
halStatus = sme_RoamDisconnect(pHddCtx->hHal,
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_IBSS_LEAVE);
else
halStatus = sme_RoamDisconnect(pHddCtx->hHal,
pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
//success implies disconnect command got queued up successfully
if(halStatus == eHAL_STATUS_SUCCESS)
{
rc = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: wait on disconnect_comp_var failed",
__func__);
}
}
else
{
hddLog(LOGE, "%s: failed to post disconnect event to SME",
__func__);
}
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);
}
if (pScanInfo != NULL && pScanInfo->mScanPending)
{
wlan_hdd_scan_abort(pAdapter);
}
if ((pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) ||
(pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)) {
wlan_hdd_cleanup_remain_on_channel_ctx(pAdapter);
}
#ifdef WLAN_OPEN_SOURCE
cancel_work_sync(&pAdapter->ipv4NotifierWorkQueue);
#endif
wlan_hdd_clean_tx_flow_control_timer(pHddCtx, pAdapter);
#ifdef WLAN_NS_OFFLOAD
#ifdef WLAN_OPEN_SOURCE
cancel_work_sync(&pAdapter->ipv6NotifierWorkQueue);
#endif
#endif
/* It is possible that the caller of this function does not
* wish to close the session
*/
if (bCloseSession)
hdd_wait_for_sme_close_sesion(pHddCtx, pAdapter);
break;
case WLAN_HDD_SOFTAP:
case WLAN_HDD_P2P_GO:
if (pHddCtx->cfg_ini->conc_custom_rule1 &&
(WLAN_HDD_SOFTAP == pAdapter->device_mode)) {
/*
* Before stopping the sap adapter, lets make sure there is
* no sap restart work pending.
*/
vos_flush_work(&pHddCtx->sap_start_work);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
FL("Canceled the pending SAP restart work"));
hdd_change_ch_avoidance_status(pHddCtx, false);
hdd_change_sap_restart_required_status(pHddCtx, false);
}
//Any softap specific cleanup here...
if (pAdapter->device_mode == WLAN_HDD_P2P_GO) {
wlan_hdd_cleanup_remain_on_channel_ctx(pAdapter);
}
hdd_set_sap_auth_offload(pAdapter, FALSE);
wlan_hdd_clean_tx_flow_control_timer(pHddCtx, pAdapter);
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->stop_bss_event);
//Stop Bss.
#ifdef WLAN_FEATURE_MBSSID
status = WLANSAP_StopBss(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter));
#else
status = WLANSAP_StopBss(pHddCtx->pvosContext);
#endif
if (VOS_IS_STATUS_SUCCESS(status))
{
status = vos_wait_single_event(&pHostapdState->stop_bss_event,
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);
vos_mem_copy(updateIE.bssid, pAdapter->macAddressCurrent.bytes,
sizeof(tSirMacAddr));
updateIE.smeSessionId = pAdapter->sessionId;
updateIE.ieBufferlength = 0;
updateIE.pAdditionIEBuffer = NULL;
updateIE.append = VOS_FALSE;
updateIE.notify = VOS_FALSE;
if (pHddCtx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_allow_suspend(pHddCtx->runtime_context.obss);
/* Probe bcn reset */
if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter),
&updateIE, eUPDATE_IE_PROBE_BCN)
== eHAL_STATUS_FAILURE) {
hddLog(LOGE, FL("Could not pass on PROBE_RSP_BCN data to PE"));
}
/* Assoc resp reset */
if (sme_UpdateAddIE(WLAN_HDD_GET_HAL_CTX(pAdapter),
&updateIE, eUPDATE_IE_ASSOC_RESP) == eHAL_STATUS_FAILURE) {
hddLog(LOGE, FL("Could not pass on ASSOC_RSP data to PE"));
}
// Reset WNI_CFG_PROBE_RSP Flags
wlan_hdd_reset_prob_rspies(pAdapter);
kfree(pAdapter->sessionCtx.ap.beacon);
pAdapter->sessionCtx.ap.beacon = NULL;
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
if (pAdapter->device_mode == WLAN_HDD_SOFTAP)
{
if(pHddCtx->ch_switch_ctx.chan_sw_timer_initialized == VOS_TRUE)
{
//Stop the channel switch timer
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer))
{
vos_timer_stop(&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer);
}
//Destroy the channel switch timer
if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pHddCtx->ch_switch_ctx.hdd_ap_chan_switch_timer)))
{
hddLog(LOGE, FL("Failed to destroy AP channel switch timer!!"));
}
pHddCtx->ch_switch_ctx.chan_sw_timer_initialized = VOS_FALSE;
}
}
#endif //WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
}
mutex_unlock(&pHddCtx->sap_lock);
cancel_work_sync(&pAdapter->ipv4NotifierWorkQueue);
#ifdef WLAN_NS_OFFLOAD
cancel_work_sync(&pAdapter->ipv6NotifierWorkQueue);
#endif
if (bCloseSession)
hdd_wait_for_sme_close_sesion(pHddCtx, pAdapter);
break;
case WLAN_HDD_OCB:
wlan_hdd_dsrc_deinit_chan_stats(pAdapter);
hdd_disconnect_tx_rx(pAdapter);
WLANTL_ClearSTAClient(WLAN_HDD_GET_CTX(pAdapter)->pvosContext,
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.staId[0]);
break;
case WLAN_HDD_MONITOR:
hdd_vir_mon_stop(pAdapter->dev);
break;
default:
break;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
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 QCA_LL_TX_FLOW_CT
/**
* hdd_adapter_abort_tx_flow() - Abort the tx flow control
* @pAdapter: pointer to hdd_adapter_t
*
* Resume tx and stop the tx flow control timer if the tx is paused and the flow
* control timer is running. This function is called by SSR to avoid the
* inconsistency of tx status before and after SSR.
*
* Return: void
*/
static void hdd_adapter_abort_tx_flow(hdd_adapter_t *pAdapter)
{
if ((pAdapter->hdd_stats.hddTxRxStats.is_txflow_paused == TRUE) &&
(VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pAdapter->tx_flow_control_timer))) {
hdd_tx_resume_timer_expired_handler(pAdapter);
vos_timer_stop(&pAdapter->tx_flow_control_timer);
}
}
#else
static void hdd_adapter_abort_tx_flow(hdd_adapter_t *pAdapter)
{
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(LOG1, FL("Disabling queues"));
hdd_adapter_abort_tx_flow(pAdapter);
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);
wlan_hdd_netif_queue_control(pAdapter,
WLAN_NETIF_TX_DISABLE,
WLAN_CONTROL_PATH);
hdd_sap_indicate_disconnect_for_sta(pAdapter);
hdd_cleanup_actionframe(pHddCtx, pAdapter);
hdd_sap_destroy_events(pAdapter);
} else
wlan_hdd_netif_queue_control(pAdapter,
WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
pAdapter->sessionCtx.station.hdd_ReassocScenario = VOS_FALSE;
hdd_deinit_tx_rx(pAdapter);
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 adapter is SAP, set session ID to invalid since SAP
* session will be cleanup during SSR.
*/
if (pAdapter->device_mode == WLAN_HDD_SOFTAP)
wlansap_set_invalid_session(
#ifdef WLAN_FEATURE_MBSSID
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter));
#else
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
#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: bss structure if found else NULL
*/
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)) \
&& !defined(WITH_BACKPORTS) && !defined(IEEE80211_PRIVACY)
struct cfg80211_bss *hdd_cfg80211_get_bss(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_cfg80211_get_bss(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
#if defined CFG80211_CONNECT_TIMEOUT_REASON_CODE || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0))
/**
* hdd_convert_timeout_reason() - Convert to kernel specific enum
* @timeout_reason: reason for connect timeout
*
* This function is used to convert host timeout
* reason enum to kernel specific enum.
*
* Return: nl timeout enum
*/
static enum nl80211_timeout_reason hdd_convert_timeout_reason(
tSirResultCodes timeout_reason)
{
switch (timeout_reason) {
case eSIR_SME_JOIN_TIMEOUT_RESULT_CODE:
return NL80211_TIMEOUT_SCAN;
case eSIR_SME_AUTH_TIMEOUT_RESULT_CODE:
return NL80211_TIMEOUT_AUTH;
case eSIR_SME_ASSOC_TIMEOUT_RESULT_CODE:
return NL80211_TIMEOUT_ASSOC;
default:
return NL80211_TIMEOUT_UNSPECIFIED;
}
}
/**
* hdd_cfg80211_connect_timeout() - API to send connection timeout reason
* @dev: network device
* @bssid: bssid to which we want to associate
* @timeout_reason: reason for connect timeout
*
* This API is used to send connection timeout reason to supplicant
*
* Return: void
*/
static void hdd_cfg80211_connect_timeout(struct net_device *dev,
const u8 *bssid,
tSirResultCodes timeout_reason)
{
enum nl80211_timeout_reason nl_timeout_reason;
nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason);
cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL,
nl_timeout_reason);
}
/**
* __hdd_connect_bss() - API to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @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
* @timeout_reason: reason for connect timeout
*
* 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, int status, gfp_t gfp,
tSirResultCodes timeout_reason)
{
enum nl80211_timeout_reason nl_timeout_reason;
nl_timeout_reason = hdd_convert_timeout_reason(timeout_reason);
cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp,
nl_timeout_reason);
}
#else /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */
#if defined CFG80211_CONNECT_TIMEOUT
static void hdd_cfg80211_connect_timeout(struct net_device *dev,
const u8 *bssid,
tSirResultCodes timeout_reason)
{
cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL);
}
#endif /* CFG80211_CONNECT_TIMEOUT */
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, int status, gfp_t gfp,
tSirResultCodes timeout_reason)
{
cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp);
}
#endif /* CFG80211_CONNECT_TIMEOUT_REASON_CODE */
/**
* hdd_connect_bss() - API to send connection status to supplicant
* @dev: network device
* @bssid: bssid to which we want to associate
* @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
* @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
* @timeout_reason: reason for connect timeout
*
* The API is a wrapper to send connection status to supplicant
*
* Return: Void
*/
#if defined CFG80211_CONNECT_TIMEOUT
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, int status, gfp_t gfp,
bool connect_timeout, tSirResultCodes timeout_reason)
{
if (connect_timeout)
hdd_cfg80211_connect_timeout(dev, bssid, timeout_reason);
else
__hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp, timeout_reason);
}
#else
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, int status, gfp_t gfp,
bool connect_timeout, tSirResultCodes timeout_reason)
{
__hdd_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp, timeout_reason);
}
#endif
#ifdef WLAN_FEATURE_FILS_SK
#ifdef CFG80211_CONNECT_DONE
#ifdef CFG80211_FILS_SK_OFFLOAD_SUPPORT
/**
* hdd_populate_fils_params() - Populate FILS keys to connect response
* @fils_params: connect response to supplicant
* @fils_kek: FILS kek
* @fils_kek_len: FILS kek length
* @pmk: FILS PMK
* @pmk_len: FILS PMK length
* @pmkid: PMKID
* @fils_seq_num: FILS Seq number
*
* Return: None
*/
static void hdd_populate_fils_params(struct cfg80211_connect_resp_params
*fils_params, const uint8_t *fils_kek,
size_t fils_kek_len, const uint8_t *pmk,
size_t pmk_len, const uint8_t *pmkid,
uint16_t fils_seq_num)
{
/* Increament seq number to be used for next FILS */
fils_params->fils_erp_next_seq_num = fils_seq_num + 1;
fils_params->update_erp_next_seq_num = true;
fils_params->fils_kek = fils_kek;
fils_params->fils_kek_len = fils_kek_len;
fils_params->pmk = pmk;
fils_params->pmk_len = pmk_len;
fils_params->pmkid = pmkid;
}
#else /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */
static inline void hdd_populate_fils_params(struct cfg80211_connect_resp_params
*fils_params, const uint8_t
*fils_kek, size_t fils_kek_len,
const uint8_t *pmk, size_t pmk_len,
const uint8_t *pmkid,
uint16_t fils_seq_num)
{ }
#endif /* CFG80211_FILS_SK_OFFLOAD_SUPPORT */
/**
* hdd_connect_done() - Wrapper API to call cfg80211_connect_done
* @dev: network device
* @bssid: bssid to which we want to associate
* @bss: cfg80211 bss info
* @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: allocation flags
* @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
* @timeout_reason: reason for connect timeout
* @roam_fils_params: FILS join response params
*
* This API is used as wrapper to send FILS key/sequence number
* params etc. to supplicant in case of FILS connection
*
* Return: None
*/
static void hdd_connect_done(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, 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, bool connect_timeout, tSirResultCodes
timeout_reason, struct fils_join_rsp_params
*roam_fils_params)
{
struct cfg80211_connect_resp_params fils_params;
vos_mem_zero(&fils_params, sizeof(fils_params));
fils_params.bssid = bssid;
if (!roam_fils_params) {
fils_params.status = WLAN_STATUS_UNSPECIFIED_FAILURE;
hdd_populate_fils_params(&fils_params, NULL, 0, NULL,
0, NULL, roam_info->fils_seq_num);
} else {
fils_params.status = status;
fils_params.timeout_reason = timeout_reason;
fils_params.req_ie = req_ie;
fils_params.req_ie_len = req_ie_len;
fils_params.resp_ie = resp_ie;
fils_params.resp_ie_len = resp_ie_len;
fils_params.bss = bss;
hdd_populate_fils_params(&fils_params, roam_fils_params->kek,
roam_fils_params->kek_len,
roam_fils_params->fils_pmk,
roam_fils_params->fils_pmk_len,
roam_fils_params->fils_pmkid,
roam_info->fils_seq_num);
}
hddLog(LOG1, "FILS indicate connect status %d seq no %d",
fils_params.status,
fils_params.fils_erp_next_seq_num);
cfg80211_connect_done(dev, &fils_params, gfp);
/* Clear all the FILS key info */
if (roam_fils_params && roam_fils_params->fils_pmk)
vos_mem_free(roam_fils_params->fils_pmk);
if (roam_fils_params)
vos_mem_free(roam_fils_params);
roam_info->fils_join_rsp = NULL;
}
#else /* CFG80211_CONNECT_DONE */
static inline void hdd_connect_done(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, 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,
bool connect_timeout, tSirResultCodes
timeout_reason, struct fils_join_rsp_params
*roam_fils_params)
{ }
#endif /* CFG80211_CONNECT_DONE */
#endif /* WLAN_FEATURE_FILS_SK */
#if defined(CFG80211_CONNECT_DONE) && defined(WLAN_FEATURE_FILS_SK)
/**
* hdd_fils_update_connect_results() - API to send fils connection status to
* supplicant.
* @dev: network device
* @bssid: bssid to which we want to associate
* @bss: cfg80211 bss info
* @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: allocation flags
* @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
* @timeout_reason: reason for connect timeout
*
* The API is a wrapper to send connection status to supplicant
*
* Return: 0 if success else failure
*/
static int hdd_fils_update_connect_results(struct net_device *dev,
const u8 *bssid,
struct cfg80211_bss *bss,
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,
bool connect_timeout,
tSirResultCodes timeout_reason)
{
ENTER();
if (!roam_info || !roam_info->is_fils_connection)
return -EINVAL;
hdd_connect_done(dev, bssid, bss, roam_info, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp, connect_timeout,
timeout_reason, roam_info->fils_join_rsp);
return 0;
}
#else /* CFG80211_CONNECT_DONE && WLAN_FEATURE_FILS_SK */
static inline int hdd_fils_update_connect_results(struct net_device *dev,
const u8 *bssid,
struct cfg80211_bss *bss,
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,
bool connect_timeout,
tSirResultCodes timeout_reason)
{
return -EINVAL;
}
#endif /* CFG80211_CONNECT_DONE && WLAN_FEATURE_FILS_SK */
/**
* 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
* @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
* @timeout_reason: reason for connect timeout
*
* The API is a wrapper to send connection status to supplicant
* and allow runtime suspend
*
* 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,
bool connect_timeout, tSirResultCodes timeout_reason)
{
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,
IEEE80211_BAND_2GHZ);
else
freq = ieee80211_channel_to_frequency(chan_no,
IEEE80211_BAND_5GHZ);
chan = ieee80211_get_channel(padapter->wdev.wiphy, freq);
bss = hdd_cfg80211_get_bss(padapter->wdev.wiphy, chan, bssid,
roam_info->u.pConnectedProfile->SSID.ssId,
roam_info->u.pConnectedProfile->SSID.length);
}
if (hdd_fils_update_connect_results(dev, bssid, bss,
roam_info, req_ie, req_ie_len, resp_ie,
resp_ie_len, status, gfp, connect_timeout,
timeout_reason) != 0) {
hdd_connect_bss(dev, bssid, bss, req_ie,
req_ie_len, resp_ie, resp_ie_len,
status, gfp, connect_timeout, timeout_reason);
}
vos_runtime_pm_allow_suspend(padapter->runtime_context.connect);
}
#else /* CFG80211_CONNECT_BSS */
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,
bool connect_timeout, tSirResultCodes timeout_reason)
{
hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev);
cfg80211_connect_result(dev, bssid, req_ie, req_ie_len,
resp_ie, resp_ie_len, status, gfp);
vos_runtime_pm_allow_suspend(padapter->runtime_context.connect);
}
#endif /* CFG80211_CONNECT_BSS */
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;
#if !defined(MSM_PLATFORM) || defined(WITH_BACKPORTS)
v_MACADDR_t bcastMac = VOS_MAC_ADDR_BROADCAST_INITIALIZER;
#endif
eConnectionState connState;
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;
pAdapter->scan_info.mScanPending = FALSE;
pAdapter->scan_info.waitScanResult = FALSE;
//Indicate disconnect event to supplicant if associated previously
if (eConnectionState_Associated == connState ||
eConnectionState_IbssConnected == connState ||
eConnectionState_NotConnected == connState ||
eConnectionState_IbssDisconnected == connState ||
eConnectionState_Disconnecting == 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, false, 0);
}
#ifdef QCA_LL_TX_FLOW_CT
if (pAdapter->tx_flow_timer_initialized == VOS_FALSE) {
vos_timer_init(&pAdapter->tx_flow_control_timer,
VOS_TIMER_TYPE_SW,
hdd_tx_resume_timer_expired_handler,
pAdapter);
pAdapter->tx_flow_timer_initialized = VOS_TRUE;
}
WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext, hdd_tx_resume_cb,
pAdapter->sessionId, (void *)pAdapter);
#endif
break;
case WLAN_HDD_SOFTAP:
if (pHddCtx->cfg_ini->sap_internal_restart) {
hdd_init_ap_mode(pAdapter, true);
#ifdef QCA_LL_TX_FLOW_CT
if (pAdapter->tx_flow_timer_initialized == VOS_FALSE) {
vos_timer_init(&pAdapter->tx_flow_control_timer,
VOS_TIMER_TYPE_SW,
hdd_softap_tx_resume_timer_expired_handler,
pAdapter);
pAdapter->tx_flow_timer_initialized = VOS_TRUE;
}
WLANTL_RegisterTXFlowControl(pHddCtx->pvosContext,
hdd_softap_tx_resume_cb,
pAdapter->sessionId,
(void *)pAdapter);
#endif
}
break;
case WLAN_HDD_P2P_GO:
#if defined(MSM_PLATFORM) && !defined(WITH_BACKPORTS)
hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send stop ap to supplicant",
__func__);
cfg80211_ap_stopped(pAdapter->dev, GFP_KERNEL);
#else
hddLog(VOS_TRACE_LEVEL_ERROR, "%s [SSR] send restart supplicant",
__func__);
/* event supplicant to restart */
cfg80211_del_sta(pAdapter->dev,
(const u8 *)&bcastMac.bytes[0], GFP_KERNEL);
#endif
break;
case WLAN_HDD_MONITOR:
/* monitor interface start */
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;
unsigned long rc;
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);
if (eConnectionState_Associated == pHddStaCtx->conn_info.connState) {
wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
}
hdd_connSetConnectionState(pAdapter, eConnectionState_NotConnected);
init_completion(&pAdapter->disconnect_comp_var);
sme_RoamDisconnect(pHddCtx->hHal, pAdapter->sessionId,
eCSR_DISCONNECT_REASON_UNSPECIFIED);
rc = wait_for_completion_timeout(
&pAdapter->disconnect_comp_var,
msecs_to_jiffies(WLAN_WAIT_TIME_DISCONNECT));
if (!rc)
hddLog(LOGE, "%s: failure waiting for disconnect_comp_var",
__func__);
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 };
tCsrBssid apBssid = { 0 }, apBssid1 = { 0 }, apBssid2 = { 0 };
v_U8_t staChannel = 0, p2pChannel = 0, apChannel = 0;
v_U8_t apChannel1 = 0, apChannel2 = 0;
const char *p2pMode = "DEV";
bool mcc_mode = FALSE;
#ifdef QCA_LL_TX_FLOW_CT
v_U8_t targetChannel = 0;
v_U8_t preAdapterChannel = 0;
v_U8_t channel24;
v_U8_t channel5;
hdd_adapter_t *preAdapterContext = NULL;
hdd_adapter_t *pAdapter2_4 = NULL;
hdd_adapter_t *pAdapter5 = NULL;
#endif /* QCA_LL_TX_FLOW_CT */
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));
#ifdef QCA_LL_TX_FLOW_CT
targetChannel = staChannel;
#endif /* QCA_LL_TX_FLOW_CT */
}
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";
#ifdef QCA_LL_TX_FLOW_CT
targetChannel = p2pChannel;
#endif /* QCA_LL_TX_FLOW_CT */
}
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));
#ifdef QCA_LL_TX_FLOW_CT
targetChannel = p2pChannel;
#endif /* QCA_LL_TX_FLOW_CT */
}
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));
if (!pHddApCtx->uBCStaId) {
apChannel1 = apChannel;
memcpy(apBssid1, apBssid, sizeof(apBssid));
} else {
apChannel2 = apChannel;
memcpy(apBssid2, apBssid, sizeof(apBssid));
}
#ifdef QCA_LL_TX_FLOW_CT
targetChannel = apChannel;
#endif /* QCA_LL_TX_FLOW_CT */
}
break;
case WLAN_HDD_IBSS:
#ifdef QCA_LL_TX_FLOW_CT
pAdapter->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxFlowLowWaterMark;
#endif
return; /* skip printing station message below */
default:
break;
}
#ifdef QCA_LL_TX_FLOW_CT
if (targetChannel)
{
/* This is first adapter detected as active
* set as default for none concurrency case */
if (!preAdapterChannel)
{
#ifdef IPA_UC_OFFLOAD
/* If IPA UC data path is enabled,
* target should reserve extra tx descriptors
* for IPA WDI data path.
* Then host data path should allow less TX packet pumping in case
* IPA WDI data path enabled */
if ((pHddCtx->cfg_ini->IpaUcOffloadEnabled) &&
(WLAN_HDD_SOFTAP == pAdapter->device_mode)) {
pAdapter->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxFlowLowWaterMark +
WLAN_TFC_IPAUC_TX_DESC_RESERVE;
} else
#endif /* IPA_UC_OFFLOAD */
{
pAdapter->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxFlowLowWaterMark;
}
pAdapter->tx_flow_high_watermark_offset =
pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset;
WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext,
pAdapter->sessionId,
pHddCtx->cfg_ini->TxFlowMaxQueueDepth);
/* Temporary set log level as error
* TX Flow control feature settled down, will lower log level */
hddLog(LOG1,
"MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode,
targetChannel,
pAdapter->tx_flow_low_watermark,
pAdapter->tx_flow_low_watermark +
pAdapter->tx_flow_high_watermark_offset,
pHddCtx->cfg_ini->TxFlowMaxQueueDepth);
preAdapterChannel = targetChannel;
preAdapterContext = pAdapter;
}
else
{
/* SCC, disable TX flow control for both
* SCC each adapter cannot reserve dedicated channel resource
* as a result, if any adapter blocked OS Q by flow control,
* blocked adapter will lost chance to recover */
if (preAdapterChannel == targetChannel)
{
/* Current adapter */
#ifdef CONFIG_PER_VDEV_TX_DESC_POOL
pAdapter->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxFlowLowWaterMark;
pAdapter->tx_flow_high_watermark_offset =
pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset;
#else
pAdapter->tx_flow_low_watermark = 0;
pAdapter->tx_flow_high_watermark_offset = 0;
#endif
WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext,
pAdapter->sessionId,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
hddLog(LOG1,
"SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode,
targetChannel,
pAdapter->tx_flow_low_watermark,
pAdapter->tx_flow_low_watermark +
pAdapter->tx_flow_high_watermark_offset,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
if (!preAdapterContext)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"SCC: Previous adapter context NULL");
continue;
}
/* Previous adapter */
#ifdef CONFIG_PER_VDEV_TX_DESC_POOL
preAdapterContext->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxFlowLowWaterMark;
preAdapterContext->tx_flow_high_watermark_offset =
pHddCtx->cfg_ini->TxFlowHighWaterMarkOffset;
#else
preAdapterContext->tx_flow_low_watermark = 0;
preAdapterContext->tx_flow_high_watermark_offset = 0;
#endif
WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext,
preAdapterContext->sessionId,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
/* Temporary set log level as error
* TX Flow control feature settled down, will lower log level */
hddLog(LOG1,
"SCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d",
hdd_device_mode_to_string(preAdapterContext->device_mode),
preAdapterContext->device_mode,
targetChannel,
preAdapterContext->tx_flow_low_watermark,
preAdapterContext->tx_flow_low_watermark +
preAdapterContext->tx_flow_high_watermark_offset,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
}
/* MCC, each adapter will have dedicated resource */
else
{
/* current channel is 2.4 */
if (targetChannel <= WLAN_HDD_TX_FLOW_CONTROL_MAX_24BAND_CH)
{
channel24 = targetChannel;
channel5 = preAdapterChannel;
pAdapter2_4 = pAdapter;
pAdapter5 = preAdapterContext;
}
/* Current channel is 5 */
else
{
channel24 = preAdapterChannel;
channel5 = targetChannel;
pAdapter2_4 = preAdapterContext;
pAdapter5 = pAdapter;
}
if (!pAdapter5)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"MCC: 5GHz adapter context NULL");
continue;
}
pAdapter5->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxHbwFlowLowWaterMark;
pAdapter5->tx_flow_high_watermark_offset =
pHddCtx->cfg_ini->TxHbwFlowHighWaterMarkOffset;
WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext,
pAdapter5->sessionId,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
/* Temporary set log level as error
* TX Flow control feature settled down, will lower log level */
hddLog(LOG1,
"MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d",
hdd_device_mode_to_string(pAdapter5->device_mode),
pAdapter5->device_mode,
channel5,
pAdapter5->tx_flow_low_watermark,
pAdapter5->tx_flow_low_watermark +
pAdapter5->tx_flow_high_watermark_offset,
pHddCtx->cfg_ini->TxHbwFlowMaxQueueDepth);
if (!pAdapter2_4)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"MCC: 2.4GHz adapter context NULL");
continue;
}
pAdapter2_4->tx_flow_low_watermark =
pHddCtx->cfg_ini->TxLbwFlowLowWaterMark;
pAdapter2_4->tx_flow_high_watermark_offset =
pHddCtx->cfg_ini->TxLbwFlowHighWaterMarkOffset;
WLANTL_SetAdapterMaxQDepth(pHddCtx->pvosContext,
pAdapter2_4->sessionId,
pHddCtx->cfg_ini->TxLbwFlowMaxQueueDepth);
/* Temporary set log level as error
* TX Flow control feature settled down, will lower log level */
hddLog(LOG1,
"MCC: MODE %s(%d), CH %d, LWM %d, HWM %d, TXQDEP %d",
hdd_device_mode_to_string(pAdapter2_4->device_mode),
pAdapter2_4->device_mode,
channel24,
pAdapter2_4->tx_flow_low_watermark,
pAdapter2_4->tx_flow_low_watermark +
pAdapter2_4->tx_flow_high_watermark_offset,
pHddCtx->cfg_ini->TxLbwFlowMaxQueueDepth);
}
}
}
targetChannel = 0;
#endif /* QCA_LL_TX_FLOW_CT */
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
/*
* Determine SCC/MSS
* Remind that this only considered STA+AP and AP+AP concurrency
* Need to expand for futher concurreny in the future
*/
if (apChannel1 > 0 && apChannel2 > 0) {
mcc_mode = apChannel1 != apChannel2;
} else if (staChannel > 0 && (apChannel1 > 0 || p2pChannel > 0)) {
mcc_mode = !(p2pChannel==staChannel || apChannel1==staChannel);
}
if (pHddCtx->mcc_mode != mcc_mode) {
#ifdef IPA_UC_STA_OFFLOAD
/* Send SCC/MCC Switching event to IPA */
hdd_ipa_send_mcc_scc_msg(pHddCtx, mcc_mode);
#endif
pHddCtx->mcc_mode = mcc_mode;
}
hddLog(VOS_TRACE_LEVEL_ERROR, "wlan(%d) " MAC_ADDRESS_STR " %s",
staChannel, MAC_ADDR_ARRAY(staBssid), mcc_mode ? "MCC" : "SCC");
if (p2pChannel > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "p2p-%s(%d) " MAC_ADDRESS_STR,
p2pMode, p2pChannel, MAC_ADDR_ARRAY(p2pBssid));
}
if (apChannel1 > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "AP(%d) " MAC_ADDRESS_STR,
apChannel1, MAC_ADDR_ARRAY(apBssid1));
}
if (apChannel2 > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR, "AP(%d) " MAC_ADDRESS_STR,
apChannel2, MAC_ADDR_ARRAY(apBssid2));
}
if (p2pChannel > 0 && apChannel1 > 0) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Error concurrent SAP %d and P2P %d which is not support"),
apChannel1, 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;
}
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_rand_macaddr(hdd_context_t *hdd_ctx,
tSirMacAddr mac_addr)
{
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 (adapter_node && status == VOS_STATUS_SUCCESS) {
adapter = adapter_node->pAdapter;
if(adapter && hdd_check_random_mac(adapter, mac_addr))
return adapter;
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
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_vdev( hdd_context_t *pHddCtx,
tANI_U32 vdev_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 == vdev_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: vdev_id %d does not exist with host",
__func__, vdev_id);
return NULL;
}
/**
* hdd_get_adapter_by_sme_session_id() - Return adapter with
* the sessionid
* @hdd_ctx: hdd cntx.
* @sme_session_id: sme session is for the adapter to get.
*
* This function is used to get the adapter with provided session id
*
* Return: adapter pointer if found
*
*/
hdd_adapter_t *hdd_get_adapter_by_sme_session_id(hdd_context_t *hdd_ctx,
uint32_t sme_session_id)
{
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
hdd_adapter_t *adapter;
VOS_STATUS vos_status;
vos_status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while ((NULL != adapter_node) &&
(VOS_STATUS_SUCCESS == vos_status)) {
adapter = adapter_node->pAdapter;
if (adapter &&
adapter->sessionId == sme_session_id)
return adapter;
vos_status =
hdd_get_next_adapter(hdd_ctx,
adapter_node, &next);
adapter_node = next;
}
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;
}
/**---------------------------------------------------------------------------
\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
supported 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;
}
/**---------------------------------------------------------------------------
\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 - callback context - 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);
}
static inline VOS_STATUS hdd_UnregisterWext_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;
if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) ||
(pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) ||
(pAdapter->device_mode == WLAN_HDD_IBSS) ||
(pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) ||
(pAdapter->device_mode == WLAN_HDD_SOFTAP) ||
(pAdapter->device_mode == WLAN_HDD_P2P_GO)) {
wlan_hdd_cfg80211_deregister_frames(pAdapter);
hdd_UnregisterWext(pAdapter->dev);
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
VOS_STATUS hdd_abort_mac_scan_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;
if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) ||
(pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) ||
(pAdapter->device_mode == WLAN_HDD_IBSS) ||
(pAdapter->device_mode == WLAN_HDD_P2P_DEVICE) ||
(pAdapter->device_mode == WLAN_HDD_SOFTAP) ||
(pAdapter->device_mode == WLAN_HDD_P2P_GO)) {
hdd_abort_mac_scan(pHddCtx, pAdapter->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
EXIT();
return VOS_STATUS_SUCCESS;
}
/**
* wlan_hdd_restart_init() - restart init
* @pHddCtx: Pointer to hdd context
*
* This function initializes restart timer/flag. An internal function.
*
* 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);
}
/**
* wlan_hdd_restart_deinit() - restart deinit
* @pHddCtx: Pointer to hdd context
*
* This function cleans up the resources used. An internal function.
*
* 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"));
}
#ifdef WLAN_NS_OFFLOAD
/**
* hdd_wlan_unregister_ip6_notifier() - unregister IP6 change notifier
* @hdd_ctx: Pointer to hdd context
*
* Return: None
*/
static void hdd_wlan_unregister_ip6_notifier(hdd_context_t *hdd_ctx)
{
unregister_inet6addr_notifier(&hdd_ctx->ipv6_notifier);
return;
}
/**
* hdd_wlan_register_ip6_notifier() - register IP6 change notifier
* @hdd_ctx: Pointer to hdd context
*
* Return: None
*/
static void hdd_wlan_register_ip6_notifier(hdd_context_t *hdd_ctx)
{
int ret;
hdd_ctx->ipv6_notifier.notifier_call = wlan_hdd_ipv6_changed;
ret = register_inet6addr_notifier(&hdd_ctx->ipv6_notifier);
if (ret)
hddLog(LOGE, FL("Failed to register IPv6 notifier"));
else
hddLog(LOG1, FL("Registered IPv6 notifier"));
return;
}
#else
/**
* hdd_wlan_unregister_ip6_notifier() - unregister IP6 change notifier
* @hdd_ctx: Pointer to hdd context
*
* Return: None
*/
static void hdd_wlan_unregister_ip6_notifier(hdd_context_t *hdd_ctx)
{
}
/**
* hdd_wlan_register_ip6_notifier() - register IP6 change notifier
* @hdd_ctx: Pointer to hdd context
*
* Return: None
*/
static void hdd_wlan_register_ip6_notifier(hdd_context_t *hdd_ctx)
{
}
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
/**
* wlan_hdd_logging_sock_activate_svc() - Activate logging
* @hdd_ctx: HDD context
*
* Activates the logging service
*
* Return: Zero in case of success, negative value otherwise
*/
static int wlan_hdd_logging_sock_activate_svc(hdd_context_t *hdd_ctx)
{
if (hdd_ctx->cfg_ini->wlanLoggingEnable) {
if (wlan_logging_sock_activate_svc(
hdd_ctx->cfg_ini->wlanLoggingFEToConsole,
hdd_ctx->cfg_ini->wlanLoggingNumBuf)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: wlan_logging_sock_activate_svc failed",
__func__);
return -EINVAL;
}
}
return 0;
}
/**
* wlan_hdd_logging_sock_deactivate_svc() - Deactivate logging
* @hdd_ctx: HDD context
*
* Deactivates the logging service
*
* Return: 0 on deactivating the logging service
*/
static int wlan_hdd_logging_sock_deactivate_svc(hdd_context_t *hdd_ctx)
{
if (hdd_ctx && hdd_ctx->cfg_ini->wlanLoggingEnable)
return wlan_logging_sock_deactivate_svc();
return 0;
}
#else
static inline int wlan_hdd_logging_sock_activate_svc(hdd_context_t *hdd_ctx)
{
return 0;
}
static inline int wlan_hdd_logging_sock_deactivate_svc(hdd_context_t *hdd_ctx)
{
return 0;
}
#endif
/**
* enum driver_unload_state - Various driver unload states
* @unload_start: Driver starting unload module.
* @unload_unregister_ip_notifier: Driver unregistering with ip notifiers.
* @unload_unregister_wext_adpater: Driver unregistering wext adapter.
* @unload_hdd_ftm_stop: Driver stopping ftm.
* @unload_hdd_ftm_close: Driver closing ftm.
* @unload_deregister_pm_ops: Driver deregistering pm ops.
* @unload_unregister_thermal_notify_cb: Driver unregistering thermal callback.
* @unload_aborting_all_scan: Driver aborting all scan.
* @unload_disable_pwr_save: Driver disable power save in firmware.
* @unload_req_full_power: Driver requesting full power mode.
* @unload_set_idle_ps_config: Driver setting imps false.
* @unload_debugfs_exit: Driver exiting debugfs.
* @unload_netdev_notifier: Driver unregistering with netdev notifiers.
* @unload_stop_all_adapter: Driver stopping all adapters.
* @unload_vos_stop: Driver stopping vos.
* @unload_vos_sched_close: Driver closing scheduler.
* @unload_vos_nv_close: Driver closing nv module.
* @unload_vos_close: Driver closing vos module.
* @unload_deinit_greep_ap: Driver deinit greep ap.
* @unload_logging_sock_deactivate_svc: Driver deactivating logging socket.
* @unload_hdd_send_status_pkg: Driver sending status packet to LPSS.
* @unload_close_cesium_nl_sock: Driver closing cesium nl socket.
* @unload_runtime_suspend_deinit: Driver deinit runtime suspend.
* @unload_close_all_adapters: Driver closing all adapter.
* @unload_ipa_cleanup: Driver performing ipa cleanup.
* @unload_flush_roc_work: Driver flush roc work.
* @unload_nl_srv_exit: Driver exit netlink service.
* @unload_wiphy_unregister: Driver unregister wiphy.
* @unload_wiphy_free: Driver free wiphy.
* @unload_subsystem_restart: Driver do subsystem restart.
* @unload_finish: Driver completed unload.
*/
enum driver_unload_state{
unload_start = 1,
unload_unregister_ip_notifier,
unload_unregister_wext_adpater,
unload_hdd_ftm_stop,
unload_hdd_ftm_close =5 ,
unload_deregister_pm_ops,
unload_unregister_thermal_notify_cb,
unload_aborting_all_scan,
unload_disable_pwr_save,
unload_req_full_power = 10,
unload_set_idle_ps_config,
unload_debugfs_exit,
unload_netdev_notifier,
unload_stop_all_adapter,
unload_vos_stop = 15,
unload_vos_sched_close,
unload_vos_nv_close,
unload_vos_close,
unload_deinit_greep_ap,
unload_logging_sock_deactivate_svc = 20,
unload_hdd_send_status_pkg,
unload_close_cesium_nl_sock,
unload_runtime_suspend_deinit,
unload_close_all_adapters,
unload_ipa_cleanup = 25,
unload_flush_roc_work,
unload_nl_srv_exit,
unload_wiphy_unregister,
unload_wiphy_free,
unload_subsystem_restart = 30,
/* keep it last*/
unload_finish = 0xff
};
/**---------------------------------------------------------------------------
\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;
struct statsContext powerContext;
unsigned long rc;
hdd_config_t *pConfig = pHddCtx->cfg_ini;
ENTER();
TRACK_UNLOAD_STATUS(unload_start);
hddLog(LOGE, FL("Unregister IPv6 notifier"));
TRACK_UNLOAD_STATUS(unload_unregister_ip_notifier);
hdd_wlan_unregister_ip6_notifier(pHddCtx);
hddLog(LOGE, FL("Unregister IPv4 notifier"));
unregister_inetaddr_notifier(&pHddCtx->ipv4_notifier);
wlan_hdd_tsf_deinit(pHddCtx);
if (VOS_FTM_MODE != hdd_get_conparam())
{
// Unloading, restart logic is no more required.
wlan_hdd_restart_deinit(pHddCtx);
}
TRACK_UNLOAD_STATUS(unload_unregister_wext_adpater);
hdd_UnregisterWext_all_adapters(pHddCtx);
if (VOS_FTM_MODE == hdd_get_conparam())
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: FTM MODE", __func__);
TRACK_UNLOAD_STATUS(unload_hdd_ftm_stop);
#if defined(QCA_WIFI_FTM)
if (hdd_ftm_stop(pHddCtx))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_ftm_stop Failed",__func__);
VOS_ASSERT(0);
}
pHddCtx->ftm.ftm_state = WLAN_FTM_STOPPED;
#endif
TRACK_UNLOAD_STATUS(unload_hdd_ftm_close);
wlan_hdd_ftm_close(pHddCtx);
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: FTM driver unloaded", __func__);
goto free_hdd_ctx;
}
TRACK_UNLOAD_STATUS(unload_deregister_pm_ops);
/* 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);
}
TRACK_UNLOAD_STATUS(unload_unregister_thermal_notify_cb);
vosStatus = hddDevTmUnregisterNotifyCallback(pHddCtx);
if ( !VOS_IS_STATUS_SUCCESS( vosStatus ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hddDevTmUnregisterNotifyCallback failed",__func__);
}
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->tdls_source_timer))
vos_timer_stop(&pHddCtx->tdls_source_timer);
vos_timer_destroy(&pHddCtx->tdls_source_timer);
/*
* 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 cancelling 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.
*/
TRACK_UNLOAD_STATUS(unload_aborting_all_scan);
hdd_abort_mac_scan_all_adapters(pHddCtx);
#ifdef FEATURE_BUS_BANDWIDTH
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->bus_bw_timer))
{
vos_timer_stop(&pHddCtx->bus_bw_timer);
hdd_rst_tcp_delack(pHddCtx);
if (pHddCtx->hbw_requested) {
vos_remove_pm_qos();
pHddCtx->hbw_requested = false;
}
}
if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pHddCtx->bus_bw_timer)))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot deallocate Bus bandwidth timer", __func__);
}
#endif
#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->skip_acs_scan_timer)) {
vos_timer_stop(&pHddCtx->skip_acs_scan_timer);
}
if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pHddCtx->skip_acs_scan_timer))) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Cannot deallocate ACS Skip timer", __func__);
}
spin_lock(&pHddCtx->acs_skip_lock);
vos_mem_free(pHddCtx->last_acs_channel_list);
pHddCtx->last_acs_channel_list = NULL;
pHddCtx->num_of_channels = 0;
spin_unlock(&pHddCtx->acs_skip_lock);
#endif
if (pConfig && !pConfig->enablePowersaveOffload)
{
TRACK_UNLOAD_STATUS(unload_disable_pwr_save);
//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;
TRACK_UNLOAD_STATUS(unload_req_full_power);
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 */
rc = wait_for_completion_timeout(
&powerContext.completion,
msecs_to_jiffies(WLAN_WAIT_TIME_POWER));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: timed out while requesting full power",
__func__);
}
}
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 */
}
}
/* 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);
}
else
{
/*
* Powersave Offload Case
* Disable Idle Power Save Mode
*/
TRACK_UNLOAD_STATUS(unload_set_idle_ps_config);
hdd_set_idle_ps_config(pHddCtx, FALSE);
}
TRACK_UNLOAD_STATUS(unload_debugfs_exit);
hdd_debugfs_exit(pHddCtx);
// Unregister the Net Device Notifier
TRACK_UNLOAD_STATUS(unload_netdev_notifier);
unregister_netdevice_notifier(&hdd_netdev_notifier);
/* Stop all adapters, this will ensure the termination of active
* connections on the interface. Make sure the vos_scheduler is
* still available to handle those control messages
*/
TRACK_UNLOAD_STATUS(unload_stop_all_adapter);
hdd_stop_all_adapters( pHddCtx );
#ifdef QCA_PKT_PROTO_TRACE
if (VOS_FTM_MODE != hdd_get_conparam())
vos_pkt_proto_trace_close();
#endif
TRACK_UNLOAD_STATUS(unload_vos_stop);
//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 ) );
}
//This requires pMac access, Call this before vos_close().
hdd_unregister_mcast_bcast_filter(pHddCtx);
TRACK_UNLOAD_STATUS(unload_vos_sched_close);
//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 ) );
}
/* Destroy the wake lock */
vos_wake_lock_destroy(&pHddCtx->rx_wake_lock);
/* Destroy the wake lock */
vos_wake_lock_destroy(&pHddCtx->sap_wake_lock);
hdd_hostapd_channel_wakelock_deinit(pHddCtx);
TRACK_UNLOAD_STATUS(unload_vos_nv_close);
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 ) );
}
TRACK_UNLOAD_STATUS(unload_vos_close);
//Close VOSS
//This frees pMac(HAL) context. There should not be any call that requires pMac access after this.
vos_close(pVosContext);
TRACK_UNLOAD_STATUS(unload_deinit_greep_ap);
hdd_wlan_green_ap_deinit(pHddCtx);
//Close Watchdog
if (pConfig && pConfig->fIsLogpEnabled)
vos_watchdog_close(pVosContext);
if (VOS_FTM_MODE != hdd_get_conparam()) {
TRACK_UNLOAD_STATUS(unload_logging_sock_deactivate_svc);
wlan_hdd_logging_sock_deactivate_svc(pHddCtx);
}
#ifdef WLAN_FEATURE_LPSS
TRACK_UNLOAD_STATUS(unload_hdd_send_status_pkg);
wlan_hdd_send_status_pkg(NULL, NULL, 0, 0);
#endif
TRACK_UNLOAD_STATUS(unload_close_cesium_nl_sock);
hdd_close_cesium_nl_sock();
TRACK_UNLOAD_STATUS(unload_runtime_suspend_deinit);
hdd_runtime_suspend_deinit(pHddCtx);
TRACK_UNLOAD_STATUS(unload_close_all_adapters);
hdd_close_all_adapters( pHddCtx );
#ifdef IPA_OFFLOAD
TRACK_UNLOAD_STATUS(unload_ipa_cleanup);
hdd_ipa_cleanup(pHddCtx);
#endif
/* 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__);
}
TRACK_UNLOAD_STATUS(unload_flush_roc_work);
/* Free up RoC request queue and flush workqueue */
vos_flush_work(&pHddCtx->rocReqWork);
hdd_list_destroy(&pHddCtx->hdd_roc_req_q);
free_hdd_ctx:
TRACK_UNLOAD_STATUS(unload_nl_srv_exit);
nl_srv_exit();
/* Free up dynamically allocated members inside HDD Adapter */
if (pHddCtx->cfg_ini) {
vos_mem_free(pHddCtx->cfg_ini);
pHddCtx->cfg_ini= NULL;
}
wlan_hdd_deinit_chan_info(pHddCtx);
wlan_hdd_deinit_tx_rx_histogram(pHddCtx);
hdd_free_probe_req_ouis(pHddCtx);
TRACK_UNLOAD_STATUS(unload_wiphy_unregister);
wiphy_unregister(wiphy) ;
wlan_hdd_cfg80211_deinit(wiphy);
TRACK_UNLOAD_STATUS(unload_wiphy_free);
wiphy_free(wiphy) ;
if (hdd_is_ssr_required())
{
#ifdef MSM_PLATFORM
#ifdef CONFIG_CNSS
/* WDI timeout had happened during unload, so SSR is needed here */
TRACK_UNLOAD_STATUS(unload_subsystem_restart);
subsystem_restart("wcnss");
#endif
#endif
msleep(5000);
}
hdd_set_ssr_required (VOS_FALSE);
TRACK_UNLOAD_STATUS(unload_finish);
}
void __hdd_wlan_exit(void)
{
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext = NULL;
ENTER();
//Get the global vos context
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if (!pVosContext)
return;
if (WLAN_IS_EPPING_ENABLED(con_mode)) {
epping_exit(pVosContext);
return;
}
if(NULL == pVosContext) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:Invalid global VOSS context", __func__);
EXIT();
return;
}
//Get the HDD context.
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD,
pVosContext);
if(NULL == pHddCtx) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:Invalid HDD Context", __func__);
EXIT();
return;
}
/* module exit should never proceed if SSR is not completed */
while(pHddCtx->isLogpInProgress){
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:SSR in Progress; block rmmod for 1 second!!!",
__func__);
msleep(1000);
}
pHddCtx->isUnloadInProgress = TRUE;
pHddCtx->driver_being_stopped = false;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
vos_set_unload_in_progress(TRUE);
hdd_close_tx_queues(pHddCtx);
//Do all the cleanup before deregistering the driver
hdd_driver_memdump_deinit();
hdd_wlan_exit(pHddCtx);
EXIT();
}
#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
/**
* hdd_skip_acs_scan_timer_handler() - skip ACS scan timer timeout handler
* @data: pointer to hdd_context_t
*
* This function will reset acs_scan_status to eSAP_DO_NEW_ACS_SCAN.
* Then new ACS request will do a fresh scan without reusing the cached
* scan information.
*
* Return: void
*/
void hdd_skip_acs_scan_timer_handler(void * data)
{
hdd_context_t *hdd_ctx = (hdd_context_t *) data;
hdd_adapter_t *ap_adapter;
hddLog(LOG1, FL("ACS Scan result expired. Reset ACS scan skip"));
hdd_ctx->skip_acs_scan_status = eSAP_DO_NEW_ACS_SCAN;
spin_lock(&hdd_ctx->acs_skip_lock);
vos_mem_free(hdd_ctx->last_acs_channel_list);
hdd_ctx->last_acs_channel_list = NULL;
hdd_ctx->num_of_channels = 0;
spin_unlock(&hdd_ctx->acs_skip_lock);
/* Get first SAP adapter to clear results */
ap_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_SOFTAP);
if (!hdd_ctx->hHal || !ap_adapter)
return;
sme_ScanFlushResult(hdd_ctx->hHal, ap_adapter->sessionId);
}
#endif
#ifdef QCA_HT_2040_COEX
/**--------------------------------------------------------------------------
\brief notify FW with HT20/HT40 mode
-------------------------------------------------------------------------*/
int hdd_wlan_set_ht2040_mode(hdd_adapter_t *pAdapter, v_U16_t staId,
v_MACADDR_t macAddrSTA, int channel_type)
{
int status;
VOS_STATUS vosStatus;
hdd_context_t *pHddCtx = NULL;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -1;
if (!pHddCtx->hHal)
return -1;
vosStatus = sme_notify_ht2040_mode(pHddCtx->hHal, staId, macAddrSTA,
pAdapter->sessionId, channel_type);
if (VOS_STATUS_SUCCESS != vosStatus) {
hddLog(LOGE, "Fail to send notification with ht2040 mode\n");
return -1;
}
return 0;
}
#endif
/**--------------------------------------------------------------------------
\brief notify FW with modem power status
-------------------------------------------------------------------------*/
int hdd_wlan_notify_modem_power_state(int state)
{
int status;
VOS_STATUS vosStatus;
v_CONTEXT_t pVosContext = NULL;
hdd_context_t *pHddCtx = NULL;
pVosContext = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if (!pVosContext)
return -1;
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return -1;
if (!pHddCtx->hHal)
return -1;
vosStatus = sme_notify_modem_power_state(pHddCtx->hHal, state);
if (VOS_STATUS_SUCCESS != vosStatus) {
hddLog(LOGE, "Fail to send notification with modem power state %d",
state);
return -1;
}
return 0;
}
/**---------------------------------------------------------------------------
\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_acquire(&wlan_wake_lock, timeout,
reason);
}
/**
* hdd_wlan_wakelock_create() -Create wakelock named as wlan
*
* Return: none
*/
void hdd_wlan_wakelock_create(void)
{
vos_wake_lock_init(&wlan_wake_lock, "wlan");
}
/**
* hdd_wlan_wakelock_destroy() -Destroy wakelock named as wlan
*
* Return: none
*/
void hdd_wlan_wakelock_destroy(void)
{
vos_wake_lock_destroy(&wlan_wake_lock);
}
/**
* wlan_hdd_wakelocks_destroy() -Destroy all the wakelocks
* @hdd_ctx: hdd context
*
* Return: none
*/
void wlan_hdd_wakelocks_destroy(hdd_context_t *hdd_ctx)
{
if (hdd_ctx) {
vos_wake_lock_destroy(&hdd_ctx->rx_wake_lock);
vos_wake_lock_destroy(&hdd_ctx->sap_wake_lock);
hdd_hostapd_channel_wakelock_deinit(hdd_ctx);
}
}
/**
* wlan_hdd_netdev_notifiers_cleanup() -unregister notifiers with kernel
* @hdd_ctx: hdd context
*
* Return: none
*/
void wlan_hdd_netdev_notifiers_cleanup(hdd_context_t * hdd_ctx)
{
if (hdd_ctx) {
hddLog(LOGE, FL("Unregister IPv6 notifier"));
hdd_wlan_unregister_ip6_notifier(hdd_ctx);
hddLog(LOGE, FL("Unregister IPv4 notifier"));
unregister_inetaddr_notifier(&hdd_ctx->ipv4_notifier);
}
unregister_netdevice_notifier(&hdd_netdev_notifier);
}
/**---------------------------------------------------------------------------
\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);
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);
}
sme_featureCapsExchange(pHddCtx->hHal);
}
} while (0);
}
/* Initialize channel list in sme based on the country code */
VOS_STATUS hdd_set_sme_chan_list(hdd_context_t *hdd_ctx)
{
return sme_init_chan_list(hdd_ctx->hHal, hdd_ctx->reg.alpha2,
hdd_ctx->reg.cc_src);
}
void hdd_set_dfs_regdomain(hdd_context_t *phddctx, bool restore)
{
if(!restore) {
if (vos_nv_get_dfs_region(&phddctx->hdd_dfs_regdomain)) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to retrieve dfs region from hdd",
__func__);
}
}
else {
if (vos_nv_set_dfs_region(phddctx->hdd_dfs_regdomain)) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: unable to set dfs region",
__func__);
}
}
}
/**
* hdd_is_5g_supported() - to know if ini configuration supports 5GHz
* @pHddCtx: Pointer to the hdd context
*
* Return: true if ini configuration 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(!pHddCtx || !pHddCtx->cfg_ini)
return true;
if (pHddCtx->cfg_ini->nBandCapability != eCSR_BAND_24)
return true;
else
return false;
}
static VOS_STATUS wlan_hdd_reg_init(hdd_context_t *hdd_ctx)
{
struct wiphy *wiphy;
VOS_STATUS status = VOS_STATUS_SUCCESS;
wiphy = hdd_ctx->wiphy;
/* 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_init_wiphy_from_eeprom();
if (!VOS_IS_STATUS_SUCCESS(status))
{
/* NV module cannot be initialized */
hddLog( VOS_TRACE_LEVEL_FATAL,
"%s: vos_init_wiphy failed", __func__);
return status;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)) || defined(WITH_BACKPORTS)
wiphy->wowlan = &wowlan_support_reg_init;
#else
wiphy->wowlan.flags = WIPHY_WOWLAN_ANY |
WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE |
WIPHY_WOWLAN_EAP_IDENTITY_REQ |
WIPHY_WOWLAN_4WAY_HANDSHAKE |
WIPHY_WOWLAN_RFKILL_RELEASE;
wiphy->wowlan.n_patterns = (WOW_MAX_FILTER_LISTS *
WOW_MAX_FILTERS_PER_LIST);
wiphy->wowlan.pattern_min_len = WOW_MIN_PATTERN_SIZE;
wiphy->wowlan.pattern_max_len = WOW_MAX_PATTERN_SIZE;
#endif
return status;
}
#ifdef FEATURE_BUS_BANDWIDTH
#ifdef QCA_SUPPORT_TXRX_HL_BUNDLE
static void hdd_set_bundle_require(uint16_t session_id, hdd_context_t *hdd_ctx,
uint64_t tx_bytes)
{
tlshim_set_bundle_require(session_id, tx_bytes,
hdd_ctx->cfg_ini->busBandwidthComputeInterval,
hdd_ctx->cfg_ini->pkt_bundle_threshold_high,
hdd_ctx->cfg_ini->pkt_bundle_threshold_low);
}
#else
static void hdd_set_bundle_require(uint16_t session_id, hdd_context_t *hdd_ctx,
uint64_t tx_bytes)
{
return;
}
#endif
void hdd_cnss_request_bus_bandwidth(hdd_context_t *pHddCtx,
const uint64_t tx_packets, const uint64_t rx_packets)
{
uint64_t total = tx_packets + rx_packets;
uint64_t temp_rx = 0;
uint64_t temp_tx = 0;
enum cnss_bus_width_type next_vote_level = CNSS_BUS_WIDTH_NONE;
enum wlan_tp_level next_rx_level = WLAN_SVC_TP_NONE;
enum wlan_tp_level next_tx_level = WLAN_SVC_TP_NONE;
struct device *dev = pHddCtx->parent_dev;
if (total > pHddCtx->cfg_ini->busBandwidthHighThreshold)
next_vote_level = CNSS_BUS_WIDTH_HIGH;
else if (total > pHddCtx->cfg_ini->busBandwidthMediumThreshold)
next_vote_level = CNSS_BUS_WIDTH_MEDIUM;
else if (total > pHddCtx->cfg_ini->busBandwidthLowThreshold)
next_vote_level = CNSS_BUS_WIDTH_LOW;
else
next_vote_level = CNSS_BUS_WIDTH_NONE;
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_vote_level
= next_vote_level;
if (pHddCtx->cur_vote_level != next_vote_level) {
hddLog(VOS_TRACE_LEVEL_DEBUG,
"%s: trigger level %d, tx_packets: %lld, rx_packets: %lld",
__func__, next_vote_level, tx_packets, rx_packets);
pHddCtx->cur_vote_level = next_vote_level;
vos_request_bus_bandwidth(dev, next_vote_level);
if (next_vote_level <= CNSS_BUS_WIDTH_LOW) {
if (pHddCtx->hbw_requested) {
vos_remove_pm_qos();
pHddCtx->hbw_requested = false;
}
if (vos_sched_handle_throughput_req(false))
hddLog(LOGE, FL("low bandwidth set rx affinity fail"));
} else {
if (!pHddCtx->hbw_requested) {
vos_request_pm_qos_type(PM_QOS_CPU_DMA_LATENCY,
DISABLE_KRAIT_IDLE_PS_VAL);
pHddCtx->hbw_requested = true;
}
if (vos_sched_handle_throughput_req(true))
hddLog(LOGE, FL("high bandwidth set rx affinity fail"));
}
}
/* fine-tuning parameters for RX Flows */
temp_rx = (rx_packets + pHddCtx->prev_rx) / 2;
pHddCtx->prev_rx = rx_packets;
if (temp_rx > pHddCtx->cfg_ini->tcpDelackThresholdHigh) {
if ((pHddCtx->cur_rx_level != WLAN_SVC_TP_HIGH) &&
(++pHddCtx->rx_high_ind_cnt == pHddCtx->cfg_ini->tcpDelackTimerCount)) {
next_rx_level = WLAN_SVC_TP_HIGH;
}
} else {
next_rx_level = WLAN_SVC_TP_LOW;
pHddCtx->rx_high_ind_cnt = 0;
}
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_rx_level
= next_rx_level;
if (pHddCtx->cur_rx_level != next_rx_level) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: TCP DELACK trigger level %d, average_rx: %llu",
__func__, next_rx_level, temp_rx);
pHddCtx->cur_rx_level = next_rx_level;
#ifdef QCA_SUPPORT_TXRX_DRIVER_TCP_DEL_ACK
if (pHddCtx->cfg_ini->del_ack_enable)
next_rx_level = WLAN_SVC_TP_LOW;
#endif
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_WLAN_TP_IND,
&next_rx_level,
sizeof(next_rx_level));
}
/* fine-tuning parameters for TX Flows */
temp_tx = (tx_packets + pHddCtx->prev_tx) / 2;
pHddCtx->prev_tx = tx_packets;
if (temp_tx > pHddCtx->cfg_ini->tcp_tx_high_tput_thres)
next_tx_level = WLAN_SVC_TP_HIGH;
else
next_tx_level = WLAN_SVC_TP_LOW;
if (pHddCtx->cur_tx_level != next_tx_level) {
hddLog(VOS_TRACE_LEVEL_DEBUG,
"%s: change TCP TX trigger level %d, average_tx: %llu ",
__func__, next_tx_level, temp_tx);
pHddCtx->cur_tx_level = next_tx_level;
wlan_hdd_send_svc_nlink_msg(pHddCtx->radio_index,
WLAN_SVC_WLAN_TP_TX_IND,
&next_tx_level,
sizeof(next_tx_level));
}
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].next_tx_level
= next_tx_level;
pHddCtx->hdd_txrx_hist_idx++;
pHddCtx->hdd_txrx_hist_idx &= NUM_TX_RX_HISTOGRAM_MASK;
}
static void hdd_bus_bw_compute_cbk(void *priv)
{
hdd_context_t *pHddCtx = (hdd_context_t *)priv;
hdd_adapter_t *pAdapter = NULL;
uint64_t tx_packets = 0, rx_packets = 0, tx_bytes = 0;
unsigned long fwd_tx_packets = 0, fwd_rx_packets = 0;
unsigned long fwd_tx_packets_diff = 0, fwd_rx_packets_diff = 0;
uint64_t total_tx = 0, total_rx = 0;
hdd_adapter_list_node_t *pAdapterNode = NULL;
VOS_STATUS status = 0;
A_STATUS ret;
v_BOOL_t connected = FALSE;
#ifdef IPA_UC_OFFLOAD
uint32_t ipa_tx_packets = 0, ipa_rx_packets = 0;
hdd_adapter_t *pValidAdapter = NULL;
#endif /* IPA_UC_OFFLOAD */
if (wlan_hdd_validate_context(pHddCtx))
return;
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;
/* Validate magic so we don't end up accessing a freed adapter.*/
if (pAdapter->magic != WLAN_HDD_ADAPTER_MAGIC)
continue;
#ifdef IPA_UC_OFFLOAD
if (NULL == pValidAdapter)
pValidAdapter = pAdapter;
#endif /* IPA_UC_OFFLOAD */
if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION ||
pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) &&
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState
!= eConnectionState_Associated) {
continue;
}
if ((pAdapter->device_mode == WLAN_HDD_SOFTAP ||
pAdapter->device_mode == WLAN_HDD_P2P_GO) &&
WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_FALSE) {
continue;
}
tx_packets += HDD_BW_GET_DIFF(pAdapter->stats.tx_packets,
pAdapter->prev_tx_packets);
tx_bytes += HDD_BW_GET_DIFF(pAdapter->stats.tx_bytes,
pAdapter->prev_tx_bytes);
rx_packets += HDD_BW_GET_DIFF(pAdapter->stats.rx_packets,
pAdapter->prev_rx_packets);
if (pAdapter->device_mode == WLAN_HDD_SOFTAP ||
pAdapter->device_mode == WLAN_HDD_P2P_GO ||
pAdapter->device_mode == WLAN_HDD_IBSS) {
ret = tlshim_get_intra_bss_fwd_pkts_count(pAdapter->sessionId,
&fwd_tx_packets, &fwd_rx_packets);
if (ret == A_OK) {
fwd_tx_packets_diff += HDD_BW_GET_DIFF(fwd_tx_packets,
pAdapter->prev_fwd_tx_packets);
fwd_rx_packets_diff += HDD_BW_GET_DIFF(fwd_tx_packets,
pAdapter->prev_fwd_rx_packets);
}
}
hdd_set_bundle_require(pAdapter->sessionId, pHddCtx, tx_bytes);
hdd_set_driver_del_ack_enable(pAdapter->sessionId, pHddCtx, rx_packets);
total_rx += pAdapter->stats.rx_packets;
total_tx += pAdapter->stats.tx_packets;
SPIN_LOCK_BH(&pHddCtx->bus_bw_lock);
pAdapter->prev_tx_packets = pAdapter->stats.tx_packets;
pAdapter->prev_tx_bytes = pAdapter->stats.tx_bytes;
pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
pAdapter->prev_fwd_tx_packets = fwd_tx_packets;
pAdapter->prev_fwd_rx_packets = fwd_rx_packets;
SPIN_UNLOCK_BH(&pHddCtx->bus_bw_lock);
connected = TRUE;
}
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].total_rx = total_rx;
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].total_tx = total_tx;
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].interval_rx = rx_packets;
pHddCtx->hdd_txrx_hist[pHddCtx->hdd_txrx_hist_idx].interval_tx = tx_packets;
/* add intra bss forwarded tx and rx packets */
tx_packets += fwd_tx_packets_diff;
rx_packets += fwd_rx_packets_diff;
#ifdef IPA_UC_OFFLOAD
hdd_ipa_uc_stat_query(pHddCtx, &ipa_tx_packets, &ipa_rx_packets);
tx_packets += (uint64_t)ipa_tx_packets;
rx_packets += (uint64_t)ipa_rx_packets;
if (pValidAdapter) {
pValidAdapter->stats.tx_packets += ipa_tx_packets;
pValidAdapter->stats.rx_packets += ipa_rx_packets;
}
#endif /* IPA_UC_OFFLOAD */
if (!connected) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"bus bandwidth timer running in disconnected state");
return;
}
hdd_cnss_request_bus_bandwidth(pHddCtx, tx_packets, rx_packets);
#ifdef IPA_OFFLOAD
hdd_ipa_set_perf_level(pHddCtx, tx_packets, rx_packets);
#ifdef IPA_UC_OFFLOAD
hdd_ipa_uc_stat_request(pValidAdapter, 2);
#endif /* IPA_UC_OFFLOAD */
#endif
vos_timer_start(&pHddCtx->bus_bw_timer,
pHddCtx->cfg_ini->busBandwidthComputeInterval);
}
#endif
/**
* wlan_hdd_init_tx_rx_histogram() - init tx/rx histogram stats
* @pHddCtx: hdd context
*
* Return: 0 for success
*/
int wlan_hdd_init_tx_rx_histogram(hdd_context_t *pHddCtx)
{
pHddCtx->hdd_txrx_hist = vos_mem_malloc(
(sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM));
if (pHddCtx->hdd_txrx_hist == NULL) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed malloc for hdd_txrx_hist",__func__);
return -ENOMEM;
}
return 0;
}
/**
* wlan_hdd_deinit_tx_rx_histogram() - deinit tx/rx histogram stats
* @pHddCtx: hdd context
*
* Return: none
*/
void wlan_hdd_deinit_tx_rx_histogram(hdd_context_t *pHddCtx)
{
if (pHddCtx->hdd_txrx_hist) {
vos_mem_free(pHddCtx->hdd_txrx_hist);
pHddCtx->hdd_txrx_hist = NULL;
}
}
void wlan_hdd_display_tx_rx_histogram(hdd_context_t *pHddCtx)
{
int i;
#ifdef FEATURE_BUS_BANDWIDTH
hddLog(VOS_TRACE_LEVEL_ERROR, "BW Interval: %d curr_index %d",
pHddCtx->cfg_ini->busBandwidthComputeInterval,
pHddCtx->hdd_txrx_hist_idx);
hddLog(VOS_TRACE_LEVEL_ERROR, "BW High TH: %d BW Med TH: %d BW Low TH: %d",
pHddCtx->cfg_ini->busBandwidthHighThreshold,
pHddCtx->cfg_ini->busBandwidthMediumThreshold,
pHddCtx->cfg_ini->busBandwidthLowThreshold);
hddLog(VOS_TRACE_LEVEL_ERROR, "TCP DEL High TH: %d TCP DEL Low TH: %d",
pHddCtx->cfg_ini->tcpDelackThresholdHigh,
pHddCtx->cfg_ini->tcpDelackThresholdLow);
#endif
hddLog(VOS_TRACE_LEVEL_ERROR,
"index, total_rx, interval_rx, total_tx, interval_tx, next_vote_level, next_rx_level, next_tx_level");
for (i=0; i < NUM_TX_RX_HISTOGRAM; i++){
hddLog(VOS_TRACE_LEVEL_ERROR,
"%d: %llu, %llu, %llu, %llu, %d, %d, %d",
i, pHddCtx->hdd_txrx_hist[i].total_rx,
pHddCtx->hdd_txrx_hist[i].interval_rx,
pHddCtx->hdd_txrx_hist[i].total_tx,
pHddCtx->hdd_txrx_hist[i].interval_tx,
pHddCtx->hdd_txrx_hist[i].next_vote_level,
pHddCtx->hdd_txrx_hist[i].next_rx_level,
pHddCtx->hdd_txrx_hist[i].next_tx_level);
}
return;
}
void wlan_hdd_clear_tx_rx_histogram(hdd_context_t *pHddCtx)
{
pHddCtx->hdd_txrx_hist_idx = 0;
vos_mem_zero(pHddCtx->hdd_txrx_hist,
(sizeof(struct hdd_tx_rx_histogram) * NUM_TX_RX_HISTOGRAM));
}
/**
* wlan_hdd_display_adapter_netif_queue_history() - display adapter's netif
* queue operation history
* @adapter: hdd adapter
*
* Return: none
*/
static inline void
wlan_hdd_display_adapter_netif_queue_history(hdd_adapter_t *adapter)
{
int i;
adf_os_time_t total, pause, unpause, curr_time;
if (!adapter)
return;
hddLog(LOGE, FL("Session_id %d device mode %d"),
adapter->sessionId, adapter->device_mode);
hddLog(LOGE, "Netif queue operation statistics:");
hddLog(LOGE, "Current pause_map value %x", adapter->pause_map);
curr_time = vos_system_ticks();
total = curr_time - adapter->start_time;
if (adapter->pause_map) {
pause = adapter->total_pause_time +
curr_time - adapter->last_time;
unpause = adapter->total_unpause_time;
} else {
unpause = adapter->total_unpause_time +
curr_time - adapter->last_time;
pause = adapter->total_pause_time;
}
hddLog(LOGE, "Total: %ums Pause: %ums Unpause: %ums",
vos_system_ticks_to_msecs(total),
vos_system_ticks_to_msecs(pause),
vos_system_ticks_to_msecs(unpause));
hddLog(LOGE, " reason_type: pause_cnt: unpause_cnt");
for (i = 0; i < WLAN_REASON_TYPE_MAX; i++) {
hddLog(LOGE, "%s: %d: %d",
hdd_reason_type_to_string(i),
adapter->queue_oper_stats[i].pause_count,
adapter->queue_oper_stats[i].unpause_count);
}
hddLog(LOGE, "Netif queue operation history: current index %d",
adapter->history_index);
hddLog(LOGE, "index: time: action_type: reason_type: pause_map");
for (i = 0; i < WLAN_HDD_MAX_HISTORY_ENTRY; i++) {
hddLog(LOGE, "%d: %u: %s: %s: %x",
i, vos_system_ticks_to_msecs(
adapter->queue_oper_history[i].time),
hdd_action_type_to_string(
adapter->queue_oper_history[i].netif_action),
hdd_reason_type_to_string(
adapter->queue_oper_history[i].netif_reason),
adapter->queue_oper_history[i].pause_map);
}
}
/**
* wlan_hdd_display_netif_queue_history() - display netif queue
* operation history
* @hdd_ctx: hdd context
*
* Return: none
*/
void wlan_hdd_display_netif_queue_history(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
VOS_STATUS status;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
wlan_hdd_display_adapter_netif_queue_history(adapter);
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
}
/**
* wlan_hdd_clear_netif_queue_history() - clear netif queue operation history
* @hdd_ctx: hdd context
*
* Return: none
*/
void wlan_hdd_clear_netif_queue_history(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *adapter = NULL;
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
VOS_STATUS status;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
vos_mem_zero(adapter->queue_oper_stats,
sizeof(adapter->queue_oper_stats));
vos_mem_zero(adapter->queue_oper_history,
sizeof(adapter->queue_oper_history));
adapter->history_index = 0;
adapter->start_time = adapter->last_time = vos_system_ticks();
adapter->total_pause_time = 0;
adapter->total_unpause_time = 0;
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
}
/**---------------------------------------------------------------------------
\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_U8 sessionId, tANI_U32 scanId,
eCsrScanStatus status)
{
ENTER();
sme_ScanFlushResult(halHandle, 0);
EXIT();
return eHAL_STATUS_SUCCESS;
}
#ifdef QCA_ARP_SPOOFING_WAR
int wlan_check_xxx(struct net_device *dev, int if_idex, void *data)
{
hddLog(VOS_TRACE_LEVEL_INFO, "Checking for arp spoof packtes\n");
return 0;
}
int hdd_filter_cb(tANI_U32 vdev_id, adf_nbuf_t skb, tANI_U32 type)
{
hdd_adapter_t *pAdapter = NULL;
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext = NULL;
int ret = 0;
switch (type) {
case RX_INTRA_BSS_FWD:
pVosContext = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
if(!pVosContext) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Global VOS context is Null", __func__);
goto out;
}
pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD, pVosContext);
if(!pHddCtx) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD context is Null",__func__);
goto out;
}
pAdapter = hdd_get_adapter_by_vdev(pHddCtx, vdev_id);
if (NULL == pAdapter) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: vdev_id %d does not exist with host",
__func__, vdev_id);
goto out;
}
if (*((unsigned short *)(skb->data + HDD_ARP_PACKET_TYPE_OFFSET))
== htons(ETH_P_ARP)) {
ret = wlan_check_xxx(pAdapter->dev, pAdapter->dev->ifindex,
skb->data);
}
break;
default:
hddLog(VOS_TRACE_LEVEL_WARN, "Invalid filter type");
goto out;
}
out:
return ret;
}
#endif
#if defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL)
/**
* wlan_hdd_bad_peer_txctl() - HDD API to initialize the bad peer
* tx flow control parameters
* @pHddCtx: HDD context which contains INI setting.
*
* Read configuation from INI setting, and then update the setting
* of SME module.
*
* Return: NULL
*/
static void wlan_hdd_bad_peer_txctl(hdd_context_t *p_hdd_ctx)
{
struct sme_bad_peer_txctl_param bad_peer_txctl;
enum sme_max_bad_peer_thresh_levels level = IEEE80211_B_LEVEL;
bad_peer_txctl.enabled =
p_hdd_ctx->cfg_ini->bad_peer_txctl_enable;
bad_peer_txctl.period =
p_hdd_ctx->cfg_ini->bad_peer_txctl_prd;
bad_peer_txctl.txq_limit =
p_hdd_ctx->cfg_ini->bad_peer_txctl_txq_lmt;
bad_peer_txctl.tgt_backoff =
p_hdd_ctx->cfg_ini->bad_peer_tgt_backoff;
bad_peer_txctl.tgt_report_prd =
p_hdd_ctx->cfg_ini->bad_peer_tgt_report_prd;
bad_peer_txctl.thresh[level].cond =
p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211b;
bad_peer_txctl.thresh[level].delta =
p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211b;
bad_peer_txctl.thresh[level].percentage =
p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211b;
bad_peer_txctl.thresh[level].thresh =
p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211b;
bad_peer_txctl.thresh[level].limit =
p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211b;
level++;
bad_peer_txctl.thresh[level].cond =
p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211ag;
bad_peer_txctl.thresh[level].delta =
p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211ag;
bad_peer_txctl.thresh[level].percentage =
p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211ag;
bad_peer_txctl.thresh[level].thresh =
p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211ag;
bad_peer_txctl.thresh[level].limit =
p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211ag;
level++;
bad_peer_txctl.thresh[level].cond =
p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211n;
bad_peer_txctl.thresh[level].delta =
p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211n;
bad_peer_txctl.thresh[level].percentage =
p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211n;
bad_peer_txctl.thresh[level].thresh =
p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211n;
bad_peer_txctl.thresh[level].limit =
p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211n;
level++;
bad_peer_txctl.thresh[level].cond =
p_hdd_ctx->cfg_ini->bad_peer_cond_ieee80211ag;
bad_peer_txctl.thresh[level].delta =
p_hdd_ctx->cfg_ini->bad_peer_delta_ieee80211ac;
bad_peer_txctl.thresh[level].percentage =
p_hdd_ctx->cfg_ini->bad_peer_pct_ieee80211ac;
bad_peer_txctl.thresh[level].thresh =
p_hdd_ctx->cfg_ini->bad_peer_tput_ieee80211ac;
bad_peer_txctl.thresh[level].limit =
p_hdd_ctx->cfg_ini->bad_peer_limit_ieee80211ac;
if (eHAL_STATUS_SUCCESS !=
sme_init_bad_peer_txctl_info(p_hdd_ctx->hHal, bad_peer_txctl)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Error while initializing bad peer Txctl infor",
__func__);
}
}
#else
static inline void wlan_hdd_bad_peer_txctl(hdd_context_t *p_hdd_ctx)
{
/* no-op */
}
#endif /* defined(CONFIG_HL_SUPPORT) && defined(QCA_BAD_PEER_TX_FLOW_CL) */
#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 = MAX_REQUEST_ID;
hdd_ctx->op_ctx.op_table[i].pattern_id = i;
}
}
#else
static void hdd_init_offloaded_packets_ctx(hdd_context_t *hdd_ctx)
{
}
#endif
#ifdef WLAN_FEATURE_WOW_PULSE
/**
* wlan_hdd_set_wow_pulse() - call SME to send wmi cmd of wow pulse
* @phddctx: hdd_context_t structure pointer
* @enable: enable or disable this behaviour
*
* Return: int
*/
static int wlan_hdd_set_wow_pulse(hdd_context_t *phddctx, bool enable)
{
hdd_config_t *pcfg_ini = phddctx->cfg_ini;
struct wow_pulse_mode wow_pulse_set_info;
VOS_STATUS status;
hddLog(LOG1, FL("wow pulse enable flag is %d"), enable);
if (false == phddctx->cfg_ini->wow_pulse_support)
return 0;
/* prepare the request to send to SME */
if (enable == true) {
wow_pulse_set_info.wow_pulse_enable = true;
wow_pulse_set_info.wow_pulse_pin =
pcfg_ini->wow_pulse_pin;
wow_pulse_set_info.wow_pulse_interval_low =
pcfg_ini->wow_pulse_interval_low;
wow_pulse_set_info.wow_pulse_interval_high=
pcfg_ini->wow_pulse_interval_high;
} else {
wow_pulse_set_info.wow_pulse_enable = false;
wow_pulse_set_info.wow_pulse_pin = 0;
wow_pulse_set_info.wow_pulse_interval_low = 0;
wow_pulse_set_info.wow_pulse_interval_high= 0;
}
hddLog(LOG1,"%s: enable %d pin %d low %d high %d",
__func__, wow_pulse_set_info.wow_pulse_enable,
wow_pulse_set_info.wow_pulse_pin,
wow_pulse_set_info.wow_pulse_interval_low,
wow_pulse_set_info.wow_pulse_interval_high);
status = sme_set_wow_pulse(&wow_pulse_set_info);
if (VOS_STATUS_E_FAILURE == status) {
hddLog(LOGE,
"%s: sme_set_wow_pulse failure!", __func__);
return -EIO;
}
hddLog(LOG2,
"%s: sme_set_wow_pulse success!", __func__);
return 0;
}
#else
static int inline wlan_hdd_set_wow_pulse(hdd_context_t *phddctx, bool enable)
{
return 0;
}
#endif
/**
* wlan_hdd_set_wakeup_gpio() - call SME to send wmi cmd of wakeup gpio
* @phddctx: hdd_context_t structure pointer
*
* Return: int
*/
static int wlan_hdd_set_wakeup_gpio(hdd_context_t *hddctx)
{
hdd_config_t *cfg_ini = hddctx->cfg_ini;
struct wakeup_gpio_mode wakeup_gpio_info;
VOS_STATUS status;
wakeup_gpio_info.host_wakeup_gpio = cfg_ini->host_wakeup_gpio;
wakeup_gpio_info.host_wakeup_type = cfg_ini->host_wakeup_type;
wakeup_gpio_info.target_wakeup_gpio = cfg_ini->target_wakeup_gpio;
wakeup_gpio_info.target_wakeup_type = cfg_ini->target_wakeup_type;
hddLog(LOG1, "%s:host_gpio %d host_type %d tar_gpio %d tar_type %d",
__func__, wakeup_gpio_info.host_wakeup_gpio,
wakeup_gpio_info.host_wakeup_type,
wakeup_gpio_info.target_wakeup_gpio,
wakeup_gpio_info.target_wakeup_type);
status = sme_set_wakeup_gpio(&wakeup_gpio_info);
if (VOS_STATUS_E_FAILURE == status) {
hddLog(LOGE,
"%s: sme_set_wakeup_gpio failure!", __func__);
return -EIO;
}
hddLog(LOG1,
"%s: sme_set_wakeup_gpio success!", __func__);
return 0;
}
/**
* hdd_state_info_dump() - prints state information of hdd layer
* @buf: buffer pointer
* @size: size of buffer to be filled
*
* This function is used to dump state information of hdd layer
*
* Return: None
*/
static void hdd_state_info_dump(char **buf_ptr, uint16_t *size)
{
v_CONTEXT_t vos_ctx_ptr;
hdd_context_t *hdd_ctx_ptr;
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
VOS_STATUS status;
hdd_station_ctx_t *hdd_sta_ctx;
hdd_adapter_t *adapter;
uint16_t len = 0;
char *buf = *buf_ptr;
/* get the global voss context */
vos_ctx_ptr = vos_get_global_context(VOS_MODULE_ID_VOSS, NULL);
if (NULL == vos_ctx_ptr) {
VOS_ASSERT(0);
return;
}
hdd_ctx_ptr = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx_ptr);
if (!hdd_ctx_ptr) {
hddLog(LOGE, FL("Failed to get hdd context "));
return;
}
hddLog(LOG1, FL("size of buffer: %d"), *size);
len += scnprintf(buf + len, *size - len,
"\n isWlanSuspended %d", hdd_ctx_ptr->isWlanSuspended);
len += scnprintf(buf + len, *size - len,
"\n isMcThreadSuspended %d",
hdd_ctx_ptr->isMcThreadSuspended);
status = hdd_get_front_adapter(hdd_ctx_ptr, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if (adapter->dev)
len += scnprintf(buf + len, *size - len,
"\n device name: %s", adapter->dev->name);
len += scnprintf(buf + len, *size - len,
"\n device_mode: %d", adapter->device_mode);
switch (adapter->device_mode) {
case WLAN_HDD_INFRA_STATION:
case WLAN_HDD_P2P_CLIENT:
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
len += scnprintf(buf + len, *size - len,
"\n connState: %d",
hdd_sta_ctx->conn_info.connState);
break;
default:
break;
}
status = hdd_get_next_adapter(hdd_ctx_ptr, adapter_node, &next);
adapter_node = next;
}
*size -= len;
*buf_ptr += len;
}
/**
* hdd_register_debug_callback() - registration function for hdd layer
* to print hdd state information
*
* Return: None
*/
static void hdd_register_debug_callback(void)
{
vos_register_debug_callback(VOS_MODULE_ID_HDD, &hdd_state_info_dump);
}
/**
* hdd_populate_random_mac_addr() - API to populate random mac addresses
* @hdd_ctx: HDD Context
* @num: Number of random mac addresses needed
*
* Generate random addresses using bit manipulation on the base mac address
*
* Return: None
*/
static void hdd_populate_random_mac_addr(hdd_context_t *hdd_ctx, uint32_t num)
{
uint32_t start_idx = VOS_MAX_CONCURRENCY_PERSONA - num;
uint32_t iter;
hdd_config_t *ini = hdd_ctx->cfg_ini;
u8 *buf = NULL;
u8 macaddr_b3, tmp_br3;
u8 *src = ini->intfMacAddr[0].bytes;
for (iter = start_idx; iter < VOS_MAX_CONCURRENCY_PERSONA; ++iter) {
buf = ini->intfMacAddr[iter].bytes;
vos_mem_copy(buf, src, VOS_MAC_ADDR_SIZE);
macaddr_b3 = buf[3];
tmp_br3 = ((macaddr_b3 >> 4 & INTF_MACADDR_MASK) + iter) &
INTF_MACADDR_MASK;
macaddr_b3 += tmp_br3;
macaddr_b3 ^= (1 << INTF_MACADDR_MASK);
buf[0] |= 0x02;
buf[3] = macaddr_b3;
hddLog(LOG1, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(buf));
}
}
/**
* hdd_cnss_wlan_mac() - API to get mac addresses from cnss platform driver
* @hdd_ctx: HDD Context
*
* API to get mac addresses from platform driver and update the driver
* structures and configure FW with the base mac address.
*
* Return: int
*/
static int hdd_cnss_wlan_mac(hdd_context_t *hdd_ctx)
{
uint32_t no_of_mac_addr, iter;
uint32_t max_mac_addr = VOS_MAX_CONCURRENCY_PERSONA;
uint32_t mac_addr_size = VOS_MAC_ADDR_SIZE;
u8 *addr, *buf;
struct device *dev = hdd_ctx->parent_dev;
hdd_config_t *ini = hdd_ctx->cfg_ini;
tSirMacAddr customMacAddr;
VOS_STATUS status;
addr = vos_get_cnss_wlan_mac_buff(dev, &no_of_mac_addr);
if (no_of_mac_addr == 0 || !addr) {
hddLog(LOG1,
FL("Platform Driver Doesn't have wlan mac addresses"));
return -EINVAL;
}
if (no_of_mac_addr > max_mac_addr)
no_of_mac_addr = max_mac_addr;
vos_mem_copy(&customMacAddr, addr, mac_addr_size);
for (iter = 0; iter < no_of_mac_addr; ++iter, addr += mac_addr_size) {
buf = ini->intfMacAddr[iter].bytes;
vos_mem_copy(buf, addr, VOS_MAC_ADDR_SIZE);
hddLog(LOG1, FL(MAC_ADDRESS_STR), MAC_ADDR_ARRAY(buf));
}
status = sme_SetCustomMacAddr(customMacAddr);
if (status != VOS_STATUS_SUCCESS)
return -EAGAIN;
if (no_of_mac_addr < max_mac_addr)
hdd_populate_random_mac_addr(hdd_ctx, max_mac_addr -
no_of_mac_addr);
return 0;
}
/**
* hdd_initialize_mac_address() - API to get wlan mac addresses
* @hdd_ctx: HDD Context
*
* Get MAC addresses from platform driver or wlan_mac.bin. If platform driver is
* provisioned with mac addresses, driver uses it, else it will use wlan_mac.bin
* to update HW MAC addresses.
*
* Return: 0 if success, errno otherwise
*/
static int hdd_initialize_mac_address(hdd_context_t *hdd_ctx)
{
VOS_STATUS status;
int ret;
ret = hdd_cnss_wlan_mac(hdd_ctx);
if (ret == 0)
return ret;
hddLog(LOGW, FL("Can't update MAC via platform driver ret: %d"), ret);
if (!hdd_ctx->cfg_ini->skip_mac_config) {
status = hdd_update_mac_config(hdd_ctx);
if (status == VOS_STATUS_SUCCESS)
return 0;
hddLog(LOGW, FL("Can't update MAC from %s status: %d"),
WLAN_MAC_FILE, status);
}
if (!vos_is_macaddr_zero(&hdd_ctx->hw_macaddr)) {
hdd_update_macaddr(hdd_ctx->cfg_ini, hdd_ctx->hw_macaddr);
} else {
tSirMacAddr customMacAddr;
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Invalid MAC passed from target, using MAC from ini"
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY(hdd_ctx->cfg_ini->intfMacAddr[0].bytes));
vos_mem_copy(&customMacAddr,
&hdd_ctx->cfg_ini->intfMacAddr[0].bytes,
VOS_MAC_ADDR_SIZE);
sme_SetCustomMacAddr(customMacAddr);
}
return 0;
}
static void hdd_get_DPD_Recaliberation_ini_param(tSmeDPDRecalParams *pDPDParam,
hdd_context_t *pHddCtx)
{
pDPDParam->enable =
pHddCtx->cfg_ini->dpd_recalib_enabled;
pDPDParam->delta_degreeHigh =
pHddCtx->cfg_ini->dpd_recalib_delta_degreehigh;
pDPDParam->delta_degreeLow =
pHddCtx->cfg_ini->dpd_recalib_delta_degreelow;
pDPDParam->cooling_time =
pHddCtx->cfg_ini->dpd_recalib_cooling_time;
pDPDParam->dpd_dur_max =
pHddCtx->cfg_ini->dpd_recalib_duration_max;
}
#ifdef FEATURE_WLAN_THERMAL_SHUTDOWN
static VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx)
{
pHddCtx->system_suspended = false;
pHddCtx->thermal_suspend_state = HDD_WLAN_THERMAL_ACTIVE;
spin_lock_init(&pHddCtx->thermal_suspend_lock);
INIT_DELAYED_WORK(&pHddCtx->thermal_suspend_work, hdd_thermal_suspend_work);
pHddCtx->thermal_suspend_wq = create_singlethread_workqueue("thermal_wq");
if (!pHddCtx->thermal_suspend_wq)
return VOS_STATUS_E_INVAL;
return VOS_STATUS_SUCCESS;
}
static void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams *pthermalParam,
hdd_context_t *pHddCtx)
{
pthermalParam->thermal_shutdown_enabled =
pHddCtx->cfg_ini->thermal_shutdown_enabled;
pthermalParam->thermal_shutdown_auto_enabled =
pHddCtx->cfg_ini->thermal_shutdown_auto_enabled;
pthermalParam->thermal_resume_threshold =
pHddCtx->cfg_ini->thermal_resume_threshold;
pthermalParam->thermal_warning_threshold =
pHddCtx->cfg_ini->thermal_warning_threshold;
pthermalParam->thermal_suspend_threshold =
pHddCtx->cfg_ini->thermal_suspend_threshold;
pthermalParam->thermal_sample_rate =
pHddCtx->cfg_ini->thermal_sample_rate;
}
#else
static inline VOS_STATUS hdd_init_thermal_ctx(hdd_context_t *pHddCtx)
{
return VOS_STATUS_SUCCESS;
}
static inline void hdd_get_thermal_shutdown_ini_param(tSmeThermalParams *pthermalParam,
hdd_context_t *pHddCtx)
{
return;
}
#endif
/**---------------------------------------------------------------------------
\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, v_VOID_t *hif_sc)
{
VOS_STATUS status;
hdd_adapter_t *pAdapter = NULL;
#ifdef WLAN_OPEN_P2P_INTERFACE
hdd_adapter_t *pP2pAdapter = NULL;
#endif
hdd_context_t *pHddCtx = NULL;
v_CONTEXT_t pVosContext= NULL;
eHalStatus hal_status;
int ret;
int i;
struct wiphy *wiphy;
unsigned long rc;
tSmeThermalParams thermalParam;
tSmeDPDRecalParams DPDParam;
tSirTxPowerLimit *hddtxlimit;
#ifdef FEATURE_WLAN_CH_AVOID
#ifdef CONFIG_CNSS
uint16_t unsafe_channel_count;
int unsafeChannelIndex;
#endif
#endif
tANI_U8 rtnl_lock_enable;
tANI_U8 reg_netdev_notifier_done = FALSE;
hdd_adapter_t *dot11_adapter = NULL;
#ifdef QCA_ARP_SPOOFING_WAR
adf_os_device_t adf_ctx;
#endif
int set_value;
struct sme_5g_band_pref_params band_pref_params;
ENTER();
if (WLAN_IS_EPPING_ENABLED(con_mode)) {
/* if epping enabled redirect start to epping module */
ret = epping_wlan_startup(dev, hif_sc);
EXIT();
return ret;
}
/*
* 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;
pHddCtx->isLoadInProgress = TRUE;
status = hdd_init_thermal_ctx(pHddCtx);
if (VOS_STATUS_SUCCESS != status)
goto err_free_hdd_context;
pHddCtx->ioctl_scan_mode = eSIR_ACTIVE_SCAN;
vos_set_wakelock_logging(false);
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
vos_set_load_in_progress(VOS_MODULE_ID_VOSS, TRUE);
/*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;
}
#ifdef QCA_ARP_SPOOFING_WAR
adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, pVosContext);
if (adf_ctx == NULL) {
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed vos_get_global_context",__func__);
goto err_free_hdd_context;
}
adf_ctx->filter_cb = (void *)hdd_filter_cb;
#endif
//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->mc_sus_event_var);
init_completion(&pHddCtx->tx_sus_event_var);
init_completion(&pHddCtx->rx_sus_event_var);
init_completion(&pHddCtx->ready_to_suspend);
#ifdef WLAN_FEATURE_EXTWOW_SUPPORT
init_completion(&pHddCtx->ready_to_extwow);
#endif
hdd_init_bpf_completion();
#ifdef FEATURE_WLAN_EXTSCAN
init_completion(&pHddCtx->ext_scan_context.response_event);
#endif /* FEATURE_WLAN_EXTSCAN */
hdd_init_ll_stats_ctx(pHddCtx);
init_completion(&pHddCtx->chain_rssi_context.response_event);
spin_lock_init(&pHddCtx->schedScan_lock);
hdd_list_init( &pHddCtx->hddAdapters, MAX_NUMBER_OF_ADAPTERS );
#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
status = wlan_hdd_init_tx_rx_histogram(pHddCtx);
if (status != 0) {
goto err_free_hdd_context;
}
spin_lock_init(&pHddCtx->dfs_lock);
spin_lock_init(&pHddCtx->sap_update_info_lock);
spin_lock_init(&pHddCtx->sta_update_info_lock);
hdd_init_offloaded_packets_ctx(pHddCtx);
// Load all config first as TL config is needed during vos_open
pHddCtx->cfg_ini = (hdd_config_t*) vos_mem_malloc(sizeof(hdd_config_t));
if(pHddCtx->cfg_ini == NULL)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: Failed kmalloc hdd_config_t",__func__);
goto err_histogram;
}
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;
}
/* If IPA HW is not existing, disable offload from INI */
if (!hdd_ipa_is_present(pHddCtx))
hdd_ipa_reset_ipaconfig(pHddCtx, 0);
if (pHddCtx->cfg_ini->probe_req_ie_whitelist)
{
if (hdd_validate_prb_req_ie_bitmap(pHddCtx))
{
/* parse ini string probe req oui */
status = hdd_parse_probe_req_ouis(pHddCtx);
if (VOS_STATUS_SUCCESS != status)
{
hddLog(LOGE, FL("Error parsing probe req ouis - Ignoring them"
" disabling white list"));
pHddCtx->cfg_ini->probe_req_ie_whitelist = false;
}
}
else
{
hddLog(LOGE, FL("invalid probe req ie bitmap and ouis,"
" disabling white list"));
pHddCtx->cfg_ini->probe_req_ie_whitelist = false;
}
}
if (0 == pHddCtx->cfg_ini->max_go_peers)
pHddCtx->cfg_ini->max_go_peers = pHddCtx->cfg_ini->max_sap_peers;
pHddCtx->max_peers = MAX(pHddCtx->cfg_ini->max_sap_peers,
pHddCtx->cfg_ini->max_go_peers);
((VosContextType*)pVosContext)->pHIFContext = hif_sc;
/* store target type and target version info in hdd ctx */
pHddCtx->target_type = ((struct ol_softc *)hif_sc)->target_type;
pHddCtx->current_intf_count=0;
pHddCtx->max_intf_count = CSR_ROAM_SESSION_MAX;
/* 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);
vos_set_fatal_event(pHddCtx->cfg_ini->enable_fatal_event);
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 (pHddCtx->cfg_ini->fhostNSOffload)
pHddCtx->ns_offload_enable = true;
/*
* If SNR Monitoring is enabled, FW has to parse all beacons
* for calculating and storing the average SNR, so set Nth beacon
* filter to 1 to enable FW to parse all the beacons
*/
if (1 == pHddCtx->cfg_ini->fEnableSNRMonitoring)
{
/* The log level is deliberately set to WARN as overriding
* nthBeaconFilter to 1 will increase power consumption 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(LOGE,
"%s: wlan_hdd_cfg80211_init return failure", __func__);
goto err_config;
}
/* Initialize struct for saving f/w log setting will be used
after ssr */
pHddCtx->fw_log_settings.enable = pHddCtx->cfg_ini->enablefwlog;
pHddCtx->fw_log_settings.dl_type = 0;
pHddCtx->fw_log_settings.dl_report = 0;
pHddCtx->fw_log_settings.dl_loglevel = 0;
pHddCtx->fw_log_settings.index = 0;
for (i = 0; i < MAX_MOD_LOGLEVEL; i++) {
pHddCtx->fw_log_settings.dl_mod_loglevel[i] = 0;
}
if (VOS_FTM_MODE != hdd_get_conparam()) {
vos_set_multicast_logging(pHddCtx->cfg_ini->multicast_host_fw_msgs);
if (wlan_hdd_logging_sock_activate_svc(pHddCtx) < 0)
goto err_sock_activate;
/*
* Update VOS trace levels based upon the code. The multicast log
* log levels of the code need not be set when the logger thread
* is not enabled.
*/
if (vos_is_multicast_logging())
wlan_logging_set_log_level();
}
/*
* Update VOS trace levels based upon the cfg.ini
*/
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);
hdd_vos_trace_enable(VOS_MODULE_ID_HDD_DATA,
pHddCtx->cfg_ini->vosTraceEnableHDDDATA);
hdd_vos_trace_enable(VOS_MODULE_ID_HDD_SAP_DATA,
pHddCtx->cfg_ini->vosTraceEnableHDDSAPDATA);
hdd_vos_trace_enable(VOS_MODULE_ID_HIF,
pHddCtx->cfg_ini->vosTraceEnableHIF);
hdd_vos_trace_enable(VOS_MODULE_ID_TXRX,
pHddCtx->cfg_ini->vosTraceEnableTXRX);
hdd_vos_trace_enable(VOS_MODULE_ID_HTC,
pHddCtx->cfg_ini->vosTraceEnableHTC);
hdd_vos_trace_enable(VOS_MODULE_ID_ADF,
pHddCtx->cfg_ini->vosTraceEnableADF);
hdd_vos_trace_enable(VOS_MODULE_ID_CFG,
pHddCtx->cfg_ini->vosTraceEnableCFG);
print_hdd_cfg(pHddCtx);
/* Initialize the nlink service */
if (wlan_hdd_nl_init(pHddCtx) != 0) {
hddLog(LOGP, FL("nl_srv_init failed"));
goto err_logging_sock;
}
vos_set_radio_index(pHddCtx->radio_index);
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_nl_srv;
}
if (VOS_STATUS_SUCCESS != hdd_ftm_start(pHddCtx))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: hdd_ftm_start Failed",__func__);
goto err_free_ftm_open;
}
} else {
//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_nl_srv;
}
}
pHddCtx->isLogpInProgress = FALSE;
vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, FALSE);
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;
}
hdd_wlan_green_ap_init(pHddCtx);
status = vos_open( &pVosContext, 0);
if ( !VOS_IS_STATUS_SUCCESS( status ))
{
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: vos_open failed", __func__);
goto err_vos_nv_close;
}
#if !defined(REMOVE_PKT_LOG)
hif_init_pdev_txrx_handle(hif_sc,
vos_get_context(VOS_MODULE_ID_TXRX, pVosContext));
#endif
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;
}
wlan_hdd_update_wiphy(wiphy, pHddCtx);
if (sme_IsFeatureSupportedByFW(DOT11AC)) {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: support 11ac", __func__);
} else {
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, "%s: not support 11ac", __func__);
if ((pHddCtx->cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac_ONLY)||
(pHddCtx->cfg_ini->dot11Mode == eHDD_DOT11_MODE_11ac)) {
pHddCtx->cfg_ini->dot11Mode = eHDD_DOT11_MODE_11n;
pHddCtx->cfg_ini->sap_p2p_11ac_override = 0;
}
}
if (0 != wlan_hdd_set_wow_pulse(pHddCtx, true)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to set wow pulse", __func__);
}
if ((pHddCtx->cfg_ini->host_wakeup_gpio !=
CFG_HOST_WAKEUP_GPIO_DEFAULT) ||
(pHddCtx->cfg_ini->target_wakeup_gpio !=
CFG_HOST_WAKEUP_GPIO_DEFAULT)) {
if (0 != wlan_hdd_set_wakeup_gpio(pHddCtx)) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Failed to set wakeup gpio", __func__);
}
}
/* Set 802.11p config
* TODO-OCB: This has been temporarily added here to ensure this paramter
* is set in CSR when we init the channel list. This should be removed
* once the 5.9 GHz channels are added to the regulatory domain.
*/
hdd_set_dot11p_config(pHddCtx);
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;
}
status = wlan_hdd_reg_init(pHddCtx);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to init channel list", __func__);
goto err_vosclose;
}
}
/* registration of wiphy dev with cfg80211 */
if (0 > wlan_hdd_cfg80211_register(pHddCtx->wiphy)) {
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: wiphy register failed", __func__);
status = VOS_STATUS_E_FAILURE;
if (VOS_FTM_MODE == hdd_get_conparam())
goto err_free_ftm_open;
else
goto err_vosclose;
}
if (VOS_FTM_MODE == hdd_get_conparam()) {
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE);
vos_set_load_in_progress(VOS_MODULE_ID_VOSS, FALSE);
pHddCtx->isLoadInProgress = FALSE;
hdd_driver_memdump_init();
hddLog(LOGE, FL("FTM driver loaded"));
wlan_comp.status = 0;
complete(&wlan_comp.wlan_start_comp);
return VOS_STATUS_SUCCESS;
}
ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS,
pHddCtx->cfg_ini->tx_chain_mask_1ss,
PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_TX_CHAIN_MASK_1SS failed %d",
__func__, ret);
}
ret = process_wma_set_command(0, WMI_PDEV_PARAM_TX_SCH_DELAY,
pHddCtx->cfg_ini->tx_sch_delay,
PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_TX_SCH_DELAY failed %d",
__func__, ret);
}
if (pHddCtx->cfg_ini->sap_get_peer_info) {
hddLog(VOS_TRACE_LEVEL_INFO,
"%s Send WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE",
__func__);
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE,
pHddCtx->cfg_ini->sap_get_peer_info,
PDEV_CMD);
if (0 != ret) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE failed %d",
__func__, ret);
}
}
ret = process_wma_set_command(0, WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
pHddCtx->cfg_ini->arp_ac_category, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE,
"%s: WMI_PDEV_PARAM_ARP_AC_OVERRIDE failed AC: %d ret: %d",
__func__, pHddCtx->cfg_ini->arp_ac_category, ret);
}
status = hdd_set_sme_chan_list(pHddCtx);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Failed to init channel list", __func__);
goto err_wiphy_unregister;
}
/* 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_wiphy_unregister;
}
ret = hdd_initialize_mac_address(pHddCtx);
if (!pHddCtx->cfg_ini->g_use_otpmac && ret) {
hddLog(LOGE,
FL("Failed to read MAC from platform driver or %s (driver load failed)"),
WLAN_MAC_FILE);
goto err_wiphy_unregister;
}
{
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_wiphy_unregister;
}
}
#ifdef IPA_OFFLOAD
#ifdef SYNC_IPA_READY
/* Check if IPA is ready before calling any IPA API */
if ((ret = ipa_register_ipa_ready_cb((void *)hdd_ipa_ready_cb,
(void *)pHddCtx)) == -EEXIST) {
hddLog(VOS_TRACE_LEVEL_FATAL, FL("IPA is ready"));
} else if (ret >= 0) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("IPA is not ready - wait until it is ready"));
init_completion(&pHddCtx->ipa_ready);
wait_for_completion(&pHddCtx->ipa_ready);
} else {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("IPA is not ready - Fail to register IPA ready callback"));
goto err_wiphy_unregister;
}
#endif
if (hdd_ipa_init(pHddCtx) == VOS_STATUS_E_FAILURE)
goto err_wiphy_unregister;
#endif
/*Start VOSS which starts up the SME/MAC/HAL modules and everything else */
status = vos_start( pHddCtx->pvosContext );
if ( !VOS_IS_STATUS_SUCCESS( status ) )
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: vos_start failed",__func__);
#ifdef IPA_OFFLOAD
goto err_ipa_cleanup;
#else
goto err_wiphy_unregister;
#endif
}
#ifdef FEATURE_WLAN_CH_AVOID
#ifdef CONFIG_CNSS
vos_get_wlan_unsafe_channel(pHddCtx->unsafe_channel_list,
&(pHddCtx->unsafe_channel_count),
sizeof(v_U16_t) * NUM_20MHZ_RF_CHANNELS);
hddLog(LOG1,"%s: num of unsafe channels is %d. ",
__func__, pHddCtx->unsafe_channel_count);
unsafe_channel_count = VOS_MIN((uint16_t)pHddCtx->unsafe_channel_count,
(uint16_t)NUM_20MHZ_RF_CHANNELS);
for (unsafeChannelIndex = 0;
unsafeChannelIndex < unsafe_channel_count;
unsafeChannelIndex++) {
hddLog(LOG1,"%s: channel %d is not safe. ",
__func__, pHddCtx->unsafe_channel_list[unsafeChannelIndex]);
}
/* Plug in avoid channel notification callback */
sme_AddChAvoidCallback(pHddCtx->hHal,
hdd_ch_avoid_cb);
#endif
#endif /* FEATURE_WLAN_CH_AVOID */
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
/* Initialize the lock*/
mutex_init(&pHddCtx->ch_switch_ctx.sap_ch_sw_lock);
/*Register the CSA Notification callback*/
sme_AddCSAIndCallback(pHddCtx->hHal, hdd_csa_notify_cb);
#endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
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;
}
#ifdef QCA_PKT_PROTO_TRACE
/* Ensure pkt tracing happen only in Non FTM mode */
vos_pkt_proto_trace_init();
#endif /* QCA_PKT_PROTO_TRACE */
#if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK)
rtnl_lock();
rtnl_lock_enable = TRUE;
#else
rtnl_lock_enable = FALSE;
#endif
/* Initialize the RoC Request queue and work. */
hdd_list_init((&pHddCtx->hdd_roc_req_q), MAX_ROC_REQ_QUEUE_ENTRY);
vos_init_delayed_work(&pHddCtx->rocReqWork, wlan_hdd_roc_request_dequeue);
vos_init_work(&pHddCtx->sap_start_work, hdd_sap_restart_handle);
if (pHddCtx->cfg_ini->dot11p_mode == WLAN_HDD_11P_STANDALONE) {
/* Create only 802.11p interface */
pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_OCB,"wlanocb%d",
wlan_hdd_get_intf_addr(pHddCtx),
NET_NAME_UNKNOWN,
rtnl_lock_enable);
} else {
pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_INFRA_STATION, "wlan%d",
wlan_hdd_get_intf_addr(pHddCtx),
NET_NAME_UNKNOWN,
rtnl_lock_enable);
#ifdef WLAN_OPEN_P2P_INTERFACE
if(VOS_MONITOR_MODE != vos_get_conparam()){
/* Open P2P device interface */
if (pAdapter != NULL &&
!hdd_cfg_is_sub20_channel_width_enabled(pHddCtx)) {
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 {
uint8_t* 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,
FL("Failed to allocate mac_address for p2p_device"));
goto err_close_adapter;
}
}
pP2pAdapter = hdd_open_adapter(pHddCtx, WLAN_HDD_P2P_DEVICE, "p2p%d",
&pHddCtx->p2pDeviceAddress.bytes[0],
NET_NAME_UNKNOWN,
rtnl_lock_enable);
if (NULL == pP2pAdapter) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("Failed to do hdd_open_adapter for P2P Device Interface"));
goto err_close_adapter;
}
}
}
#endif /* WLAN_OPEN_P2P_INTERFACE */
/* Open 802.11p Interface */
if (pAdapter != NULL) {
if (pHddCtx->cfg_ini->dot11p_mode == WLAN_HDD_11P_CONCURRENT) {
dot11_adapter = hdd_open_adapter(pHddCtx, WLAN_HDD_OCB, "wlanocb%d",
wlan_hdd_get_intf_addr(pHddCtx),
NET_NAME_UNKNOWN,
rtnl_lock_enable);
if (dot11_adapter == NULL) {
hddLog(VOS_TRACE_LEVEL_FATAL,
FL("hdd_open_adapter() failed for 802.11p Interface"));
goto err_close_adapter;
}
}
}
}
if( pAdapter == NULL )
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: hdd_open_adapter failed", __func__);
goto err_close_adapter;
}
/* Target hw version/revision would only be retrieved after
firmware download */
hif_get_hw_info(hif_sc, &pHddCtx->target_hw_version,
&pHddCtx->target_hw_revision);
/* Get the wlan hw/fw version */
hdd_wlan_get_version(pAdapter, NULL, NULL);
/* pass target_fw_version to HIF layer */
hif_set_fw_info(hif_sc, pHddCtx->target_fw_version);
if (country_code)
{
eHalStatus ret;
INIT_COMPLETION(pAdapter->change_country_code);
hdd_checkandupdate_dfssetting(pAdapter, country_code);
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)
{
rc = wait_for_completion_timeout(
&pAdapter->change_country_code,
msecs_to_jiffies(WLAN_WAIT_TIME_COUNTRY));
if (!rc) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME while setting country code timed out", __func__);
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: SME Change Country code from module param fail ret=%d", __func__, ret);
ret = -EINVAL;
}
}
#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
#ifdef FEATURE_WLAN_SCAN_PNO
/*SME must send channel update configuration to FW*/
sme_UpdateChannelConfig(pHddCtx->hHal);
#endif
sme_Register11dScanDoneCallback(pHddCtx->hHal, hdd_11d_scan_done);
/* Fwr capabilities received, Set the Dot11 mode */
sme_SetDefDot11Mode(pHddCtx->hHal);
/* 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__);
goto err_close_adapter;
}
/* 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 for riva power on lock to platform driver */
if (req_riva_power_on_lock("wlan"))
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: req riva power on lock failed",
__func__);
goto err_unregister_pmops;
}
#if !defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK)
// 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_free_power_on_lock;
}
reg_netdev_notifier_done = TRUE;
#endif
#ifdef WLAN_KD_READY_NOTIFIER
pHddCtx->kd_nl_init = 1;
#endif /* WLAN_KD_READY_NOTIFIER */
#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_reg_netdev;
}
#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_reg_netdev;
}
#endif
if (hdd_open_cesium_nl_sock() < 0)
hddLog(VOS_TRACE_LEVEL_WARN, FL("hdd_open_cesium_nl_sock failed"));
//Initialize the CNSS-DIAG service
if (cnss_diag_activate_service() < 0)
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: cnss_diag_activate_service failed", __func__);
goto err_close_cesium;
}
hdd_register_mcast_bcast_filter(pHddCtx);
if (VOS_STA_SAP_MODE != hdd_get_conparam())
{
/* Action frame registered in one adapter which will
* applicable to all interfaces
*/
wlan_hdd_cfg80211_register_frames(pAdapter);
}
mutex_init(&pHddCtx->sap_lock);
#if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK)
if (rtnl_lock_enable == TRUE) {
rtnl_lock_enable = FALSE;
rtnl_unlock();
}
/* 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_close_cesium;
}
reg_netdev_notifier_done = TRUE;
#endif
/* Initialize the wake lcok */
vos_wake_lock_init(&pHddCtx->rx_wake_lock,
"qcom_rx_wakelock");
/* Initialize the wake lcok */
vos_wake_lock_init(&pHddCtx->sap_wake_lock,
"qcom_sap_wakelock");
hdd_hostapd_channel_wakelock_init(pHddCtx);
// Initialize the restart logic
wlan_hdd_restart_init(pHddCtx);
if(pHddCtx->cfg_ini->enablePowersaveOffload)
{
hdd_set_idle_ps_config(pHddCtx, TRUE);
}
if ((pHddCtx->cfg_ini->enable_ac_txq_optimize >> 4) & 0x01)
sme_set_ac_txq_optimize(pHddCtx->hHal,
&pHddCtx->cfg_ini->enable_ac_txq_optimize);
if (pHddCtx->cfg_ini->enable_go_cts2self_for_sta)
sme_set_cts2self_for_p2p_go(pHddCtx->hHal);
/* Reset previous stats before turning on/off */
vos_mem_set(&pAdapter->mib_stats,
sizeof(pAdapter->mib_stats), 0);
hal_status = sme_set_mib_stats_enable(pHddCtx->hHal,
pHddCtx->cfg_ini->mib_stats_enabled);
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(VOS_TRACE_LEVEL_ERROR, FL("set mib stats failed"));
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
if (pHddCtx->cfg_ini->WlanAutoShutdown != 0)
if (sme_set_auto_shutdown_cb(pHddCtx->hHal, wlan_hdd_auto_shutdown_cb)
!= eHAL_STATUS_SUCCESS)
hddLog(LOGE, FL("Auto shutdown feature could not be enabled"));
#endif
#ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
status = vos_timer_init(&pHddCtx->skip_acs_scan_timer, VOS_TIMER_TYPE_SW,
hdd_skip_acs_scan_timer_handler, (void *)pHddCtx);
if (!VOS_IS_STATUS_SUCCESS(status))
hddLog(LOGE, FL("Failed to init ACS Skip timer\n"));
spin_lock_init(&pHddCtx->acs_skip_lock);
#endif
#ifdef WLAN_FEATURE_NAN
wlan_hdd_cfg80211_nan_init(pHddCtx);
#endif
/* Thermal Mitigation */
thermalParam.smeThermalMgmtEnabled =
pHddCtx->cfg_ini->thermalMitigationEnable;
thermalParam.smeThrottlePeriod = pHddCtx->cfg_ini->throttlePeriod;
thermalParam.sme_throttle_duty_cycle_tbl[0]=
pHddCtx->cfg_ini->throttle_dutycycle_level0;
thermalParam.sme_throttle_duty_cycle_tbl[1]=
pHddCtx->cfg_ini->throttle_dutycycle_level1;
thermalParam.sme_throttle_duty_cycle_tbl[2]=
pHddCtx->cfg_ini->throttle_dutycycle_level2;
thermalParam.sme_throttle_duty_cycle_tbl[3]=
pHddCtx->cfg_ini->throttle_dutycycle_level3;
thermalParam.smeThermalLevels[0].smeMinTempThreshold =
pHddCtx->cfg_ini->thermalTempMinLevel0;
thermalParam.smeThermalLevels[0].smeMaxTempThreshold =
pHddCtx->cfg_ini->thermalTempMaxLevel0;
thermalParam.smeThermalLevels[1].smeMinTempThreshold =
pHddCtx->cfg_ini->thermalTempMinLevel1;
thermalParam.smeThermalLevels[1].smeMaxTempThreshold =
pHddCtx->cfg_ini->thermalTempMaxLevel1;
thermalParam.smeThermalLevels[2].smeMinTempThreshold =
pHddCtx->cfg_ini->thermalTempMinLevel2;
thermalParam.smeThermalLevels[2].smeMaxTempThreshold =
pHddCtx->cfg_ini->thermalTempMaxLevel2;
thermalParam.smeThermalLevels[3].smeMinTempThreshold =
pHddCtx->cfg_ini->thermalTempMinLevel3;
thermalParam.smeThermalLevels[3].smeMaxTempThreshold =
pHddCtx->cfg_ini->thermalTempMaxLevel3;
hdd_get_thermal_shutdown_ini_param(&thermalParam, pHddCtx);
if (eHAL_STATUS_SUCCESS != sme_InitThermalInfo(pHddCtx->hHal,thermalParam))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Error while initializing thermal information", __func__);
}
/* Runtime DPD Recaliberation config*/
hdd_get_DPD_Recaliberation_ini_param(&DPDParam, pHddCtx);
if (eHAL_STATUS_SUCCESS != sme_InitDPDRecalInfo(pHddCtx->hHal, DPDParam))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Error while initializing Runtime DPD Recaliberation information", __func__);
}
/* Plug in set thermal level callback */
sme_add_set_thermal_level_callback(pHddCtx->hHal,
(tSmeSetThermalLevelCallback)hdd_set_thermal_level_cb);
sme_add_thermal_temperature_ind_callback(pHddCtx->hHal,
(tSmeThermalTempIndCb)hdd_thermal_temp_ind_event_cb);
/* Bad peer tx flow control */
wlan_hdd_bad_peer_txctl(pHddCtx);
/* SAR power limit */
hddtxlimit = vos_mem_malloc(sizeof(tSirTxPowerLimit));
if (!hddtxlimit)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Memory allocation for TxPowerLimit "
"failed!", __func__);
goto err_close_cesium;
}
hddtxlimit->txPower2g = pHddCtx->cfg_ini->TxPower2g;
hddtxlimit->txPower5g = pHddCtx->cfg_ini->TxPower5g;
if (eHAL_STATUS_SUCCESS != sme_TxpowerLimit(pHddCtx->hHal,hddtxlimit))
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: Error setting txlimit in sme", __func__);
}
vos_timer_init(&pHddCtx->tdls_source_timer, VOS_TIMER_TYPE_SW,
wlan_hdd_change_tdls_mode, (void *)pHddCtx);
#ifdef FEATURE_BUS_BANDWIDTH
spin_lock_init(&pHddCtx->bus_bw_lock);
vos_timer_init(&pHddCtx->bus_bw_timer,
VOS_TIMER_TYPE_SW,
hdd_bus_bw_compute_cbk,
(void *)pHddCtx);
#endif
#ifdef WLAN_FEATURE_STATS_EXT
wlan_hdd_cfg80211_stats_ext_init(pHddCtx);
#endif
#ifdef FEATURE_WLAN_EXTSCAN
sme_ExtScanRegisterCallback(pHddCtx->hHal,
wlan_hdd_cfg80211_extscan_callback);
#endif /* FEATURE_WLAN_EXTSCAN */
sme_chain_rssi_register_callback(pHddCtx->hHal,
wlan_hdd_cfg80211_chainrssi_callback);
sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached);
wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx);
wlan_hdd_tsf_init(pHddCtx);
#ifdef WLAN_FEATURE_LPSS
wlan_hdd_send_all_scan_intf_info(pHddCtx);
wlan_hdd_send_version_pkg(pHddCtx->target_fw_version,
pHddCtx->target_hw_version,
pHddCtx->target_hw_name);
#endif
if (WLAN_HDD_RX_HANDLE_RPS == pHddCtx->cfg_ini->rxhandle)
hdd_set_rps_cpu_mask(pHddCtx);
hal_status = sme_set_lost_link_info_cb(pHddCtx->hHal,
hdd_lost_link_info_cb);
/* print error and not block the startup process */
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(LOGE, "%s: set lost link info callback failed", __func__);
hal_status = sme_set_smps_force_mode_cb(pHddCtx->hHal,
hdd_smps_force_mode_cb);
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(LOGE, FL("set smps force mode callback failed"));
hal_status = sme_bpf_offload_register_callback(pHddCtx->hHal,
hdd_get_bpf_offload_cb);
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(LOGE, FL("set bpf offload callback failed"));
wlan_hdd_dcc_register_for_dcc_stats_event(pHddCtx);
wlan_hdd_init_chan_info(pHddCtx);
/*
* Register IPv6 notifier to notify if any change in IP
* So that we can reconfigure the offload parameters
*/
hdd_wlan_register_ip6_notifier(pHddCtx);
/*
* 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(LOGE, FL("Failed to register IPv4 notifier"));
else
hddLog(LOG1, FL("Registered IPv4 notifier"));
ol_pktlog_init(hif_sc);
/*
* Send btc page and wlan (p2p/sta/sap) interval to firmware if
* relevant parameters set in ini file.
*/
hdd_set_btc_bt_wlan_interval(pHddCtx);
hdd_runtime_suspend_init(pHddCtx);
pHddCtx->isLoadInProgress = FALSE;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, FALSE);
vos_set_load_in_progress(VOS_MODULE_ID_VOSS, FALSE);
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);
}
if (pHddCtx->cfg_ini->enable_dynamic_sta_chainmask)
hdd_decide_dynamic_chain_mask(pHddCtx,
HDD_ANTENNA_MODE_1X1);
hdd_driver_memdump_init();
if (pHddCtx->cfg_ini->goptimize_chan_avoid_event) {
hal_status = sme_enable_disable_chanavoidind_event(pHddCtx->hHal, 0);
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(LOGE, FL("Failed to disable Chan Avoidance Indcation"));
}
if (pHddCtx->cfg_ini->enable_5g_band_pref) {
band_pref_params.rssi_boost_threshold_5g =
pHddCtx->cfg_ini->rssi_boost_threshold_5g;
band_pref_params.rssi_boost_factor_5g =
pHddCtx->cfg_ini->rssi_boost_factor_5g;
band_pref_params.max_rssi_boost_5g =
pHddCtx->cfg_ini->max_rssi_boost_5g;
band_pref_params.rssi_penalize_threshold_5g =
pHddCtx->cfg_ini->rssi_penalize_threshold_5g;
band_pref_params.rssi_penalize_factor_5g =
pHddCtx->cfg_ini->rssi_penalize_factor_5g;
band_pref_params.max_rssi_penalize_5g =
pHddCtx->cfg_ini->max_rssi_penalize_5g;
sme_set_5g_band_pref(pHddCtx->hHal, &band_pref_params);
}
if (pHddCtx->cfg_ini->sifs_burst_duration) {
set_value = (SIFS_BURST_DUR_MULTIPLIER) *
pHddCtx->cfg_ini->sifs_burst_duration;
if ((set_value > 0) && (set_value <= SIFS_BURST_DUR_MAX))
process_wma_set_command(0, (int)WMI_PDEV_PARAM_BURST_DUR,
set_value, PDEV_CMD);
}
if (pHddCtx->cfg_ini->max_mpdus_inampdu) {
set_value = pHddCtx->cfg_ini->max_mpdus_inampdu;
process_wma_set_command(0, (int)WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU,
set_value, PDEV_CMD);
}
if (pHddCtx->cfg_ini->enable_rts_sifsbursting) {
set_value = pHddCtx->cfg_ini->enable_rts_sifsbursting;
process_wma_set_command(0, (int)WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING,
set_value, PDEV_CMD);
}
if (hdd_wlan_enable_egap(pHddCtx))
hddLog(LOGE, FL("enhance green ap is not enabled"));
/* set chip power save failure detected callback */
sme_set_chip_pwr_save_fail_cb(pHddCtx->hHal,
hdd_chip_pwr_save_fail_detected_cb);
wlan_comp.status = 0;
complete(&wlan_comp.wlan_start_comp);
goto success;
err_close_cesium:
hdd_close_cesium_nl_sock();
err_reg_netdev:
if (rtnl_lock_enable == TRUE) {
rtnl_lock_enable = FALSE;
rtnl_unlock();
}
if (reg_netdev_notifier_done == TRUE) {
unregister_netdevice_notifier(&hdd_netdev_notifier);
reg_netdev_notifier_done = FALSE;
}
#if !defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK)
err_free_power_on_lock:
#endif
free_riva_power_on_lock("wlan");
err_unregister_pmops:
hddDevTmUnregisterNotifyCallback(pHddCtx);
hddDeregisterPmOps(pHddCtx);
hdd_debugfs_exit(pHddCtx);
err_close_adapter:
#if defined(CONFIG_HDD_INIT_WITH_RTNL_LOCK)
if (rtnl_lock_enable == TRUE) {
rtnl_lock_enable = FALSE;
rtnl_unlock();
}
#endif
hdd_close_all_adapters( pHddCtx );
#ifdef QCA_PKT_PROTO_TRACE
if (VOS_FTM_MODE != hdd_get_conparam())
vos_pkt_proto_trace_close();
#endif /* QCA_PKT_PROTO_TRACE */
err_vosstop:
vos_stop(pVosContext);
#ifdef IPA_OFFLOAD
err_ipa_cleanup:
hdd_ipa_cleanup(pHddCtx);
#endif
err_wiphy_unregister:
wiphy_unregister(wiphy);
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();
hdd_wlan_green_ap_deinit(pHddCtx);
err_wdclose:
if(pHddCtx->cfg_ini->fIsLogpEnabled)
vos_watchdog_close(pVosContext);
if (VOS_FTM_MODE == hdd_get_conparam())
{
#if defined(QCA_WIFI_FTM)
err_free_ftm_open:
wlan_hdd_ftm_close(pHddCtx);
#endif
}
err_nl_srv:
nl_srv_exit();
err_logging_sock:
if (VOS_FTM_MODE != hdd_get_conparam())
wlan_hdd_logging_sock_deactivate_svc(pHddCtx);
err_sock_activate:
wlan_hdd_cfg80211_deinit(wiphy);
err_config:
vos_mem_free(pHddCtx->cfg_ini);
pHddCtx->cfg_ini= NULL;
err_histogram:
wlan_hdd_deinit_tx_rx_histogram(pHddCtx);
err_free_hdd_context:
/* wiphy_free() will free the HDD context so remove global reference */
if (pVosContext) {
hdd_free_probe_req_ouis(pHddCtx);
((VosContextType*)(pVosContext))->pHDDContext = NULL;
}
wiphy_free(wiphy) ;
//kfree(wdev) ;
VOS_BUG(1);
if (hdd_is_ssr_required())
{
#ifdef MSM_PLATFORM
#ifdef CONFIG_CNSS
/* WDI timeout had happened during load, so SSR is needed here */
subsystem_restart("wcnss");
#endif
#endif
msleep(5000);
}
hdd_set_ssr_required (VOS_FALSE);
wlan_comp.status = -EAGAIN;
complete(&wlan_comp.wlan_start_comp);
return -EIO;
success:
EXIT();
return 0;
}
/* accommodate the request firmware bin time out 2 min */
#define REQUEST_FWR_TIMEOUT 120000
#define HDD_WLAN_START_WAIT_TIME 5000
/**
* hdd_hif_register_driver() - API for HDD to register with HIF
*
* API for HDD to register with HIF layer
*
* Return: success/failure
*/
int hdd_hif_register_driver(void)
{
int ret;
unsigned long rc, timeout;
init_completion(&wlan_comp.wlan_start_comp);
wlan_comp.status = 0;
ret = hif_register_driver();
if (ret) {
hddLog(LOGE, FL("HIF registration failed"));
return ret;
}
#ifdef HDD_WLAN_WAIT_TIME
timeout = msecs_to_jiffies(HDD_WLAN_WAIT_TIME);
#else
timeout = msecs_to_jiffies(HDD_WLAN_START_WAIT_TIME);
#endif
rc = wait_for_completion_timeout(&wlan_comp.wlan_start_comp, timeout);
if (!rc) {
hddLog(LOGE, FL("hif registration timedout"));
return -EAGAIN;
}
if (wlan_comp.status)
hddLog(LOGE,
FL("hdd_wlan_startup failed status:%d jiffies_left:%lu"),
wlan_comp.status, rc);
return wlan_comp.status;
}
#ifdef TIMER_MANAGER
static inline void hdd_timer_exit(void)
{
vos_timer_exit();
}
#else
static inline void hdd_timer_exit(void)
{
}
#endif
#ifdef MEMORY_DEBUG
static inline void hdd_mem_exit(void)
{
adf_net_buf_debug_exit();
vos_mem_exit();
}
#else
static inline void hdd_mem_exit(void)
{
}
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
static inline void hdd_logging_sock_deinit_svc(void)
{
wlan_logging_sock_deinit_svc();
}
#else
static inline void hdd_logging_sock_deinit_svc(void)
{
}
#endif
static int hdd_register_fail_clean_up(v_CONTEXT_t vos_context)
{
hif_unregister_driver();
vos_preClose(&vos_context);
hdd_timer_exit();
hdd_mem_exit();
hdd_logging_sock_deinit_svc();
return -ENODEV;
}
/**---------------------------------------------------------------------------
\brief hdd_driver_init() - Core Driver Init Function
This is the driver entry point - called in different time line 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;
int ret_status = 0;
u_int64_t start;
start = adf_get_boottime();
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_init_svc();
#endif
ENTER();
#ifdef TIMER_MANAGER
vos_timer_manager_init();
#endif
#ifdef MEMORY_DEBUG
vos_mem_init();
adf_net_buf_debug_init();
#endif
hdd_wlan_wakelock_create();
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
/*
* The Krait is going to Idle/Stand Alone Power Save
* more aggressively which is resulting in the longer driver load time.
* The Fix is to not allow Krait to enter Idle Power Save during driver load.
* at a time, and wait for the completion interrupt to start the next
* transfer. During this phase, the KRAIT is entering IDLE/StandAlone(SA)
* Power Save(PS). The delay incurred for resuming from IDLE/SA PS is
* huge during driver load. So prevent APPS IDLE/SA PS during driver
* load for reducing interrupt latency.
*/
vos_request_pm_qos_type(PM_QOS_CPU_DMA_LATENCY, DISABLE_KRAIT_IDLE_PS_VAL);
vos_ssr_protect_init();
pr_info("%s: loading driver v%s\n", WLAN_MODULE_NAME,
QWLAN_VERSIONSTR TIMER_MANAGER_STR MEMORY_DEBUG_STR);
do {
#ifndef MODULE
/* For statically linked driver, call hdd_set_conparam to update curr_con_mode
*/
hdd_set_conparam((v_UINT_t)con_mode);
if (WLAN_IS_EPPING_ENABLED(con_mode)) {
ret_status = epping_driver_init(con_mode, &wlan_wake_lock,
WLAN_MODULE_NAME);
if (ret_status < 0) {
vos_remove_pm_qos();
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
hdd_wlan_wakelock_destroy();
}
return ret_status;
}
#else
if (WLAN_IS_EPPING_ENABLED(hdd_get_conparam())) {
ret_status = epping_driver_init(hdd_get_conparam(),
&wlan_wake_lock, WLAN_MODULE_NAME);
if (ret_status < 0) {
vos_remove_pm_qos();
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
hdd_wlan_wakelock_destroy();
}
return ret_status;
}
#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__);
vos_remove_pm_qos();
ret_status = -1;
break;
}
#ifdef HDD_TRACE_RECORD
MTRACE(hddTraceInit());
#endif
hdd_register_debug_callback();
ret_status = hdd_hif_register_driver();
vos_remove_pm_qos();
if (ret_status == 0) {
pr_info("%s: driver loaded in %lld\n", WLAN_MODULE_NAME,
adf_get_boottime() - start);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
return 0;
}
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: WLAN Driver Initialization failed",
__func__);
ret_status = hdd_register_fail_clean_up(pVosContext);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
hdd_wlan_wakelock_destroy();
} while (0);
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 */
static struct timer_list unload_timer;
static bool unload_timer_started;
#ifdef CONFIG_SLUB_DEBUG_ON
#define HDD_UNLOAD_WAIT_TIME 35000
#else
#define HDD_UNLOAD_WAIT_TIME 30000
#endif
/**
* hdd_unload_timer_init() - API to initialize unload timer
*
* initialize unload timer
*
* Return: None
*/
static void hdd_unload_timer_init(void)
{
init_timer(&unload_timer);
}
/**
* hdd_unload_timer_del() - API to Delete unload timer
*
* Delete unload timer
*
* Return: None
*/
static void hdd_unload_timer_del(void)
{
del_timer(&unload_timer);
unload_timer_started = false;
}
/**
* hdd_unload_timer_cb() - Unload timer callback function
*
* Unload timer callback function
*
* Return: None
*/
static void hdd_unload_timer_cb(unsigned long data)
{
v_CONTEXT_t vos_context = NULL;
hdd_context_t *hdd_ctx = NULL;
pr_err("HDD unload timer expired!,current unload status: %d",
g_current_unload_state);
/* Get the global VOSS context. */
vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
if(vos_context) {
hdd_ctx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD,
vos_context );
if(hdd_ctx)
pr_err("Driver loading: %d unloading:%d logp_in_progress: %d",
hdd_ctx->isLoadInProgress,
hdd_ctx->isUnloadInProgress,
hdd_ctx->isLogpInProgress);
} else
pr_err("%s: Global VOS context is Null", __func__);
#ifdef CONFIG_SLUB_DEBUG_ON
VOS_BUG(0);
#endif
}
/**
* hdd_unload_timer_start() - API to start unload timer
* @msec: timer interval in msec units
*
* API to start unload timer
*
* Return: None
*/
static void hdd_unload_timer_start(int msec)
{
if(unload_timer_started)
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Starting unload timer when it's running!",
__func__);
unload_timer.expires = jiffies + msecs_to_jiffies(msec);
unload_timer.function = hdd_unload_timer_cb;
add_timer(&unload_timer);
unload_timer_started = true;
}
/**---------------------------------------------------------------------------
\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 user space)
\param - None
\return - None
--------------------------------------------------------------------------*/
static void hdd_driver_exit(void)
{
hdd_context_t *pHddCtx = NULL;
int retry = 0;
v_CONTEXT_t pVosContext = NULL;
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;
}
if (WLAN_IS_EPPING_ENABLED(con_mode)) {
epping_driver_exit(pVosContext);
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
{
hdd_thermal_suspend_cleanup(pHddCtx);
/*
* Check IPA HW pipe shutdown properly or not
* If not, force shut down HW pipe
*/
hdd_ipa_uc_force_pipe_shutdown(pHddCtx);
pHddCtx->driver_being_stopped = false;
rtnl_lock();
pHddCtx->isUnloadInProgress = TRUE;
vos_set_load_unload_in_progress(VOS_MODULE_ID_VOSS, TRUE);
vos_set_unload_in_progress(TRUE);
rtnl_unlock();
while(pHddCtx->isLogpInProgress ||
vos_is_logp_in_progress(VOS_MODULE_ID_VOSS, NULL)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:SSR in Progress; block rmmod for 1 second!!!", __func__);
msleep(1000);
if (retry++ == HDD_MOD_EXIT_SSR_MAX_RETRIES) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"%s:SSR never completed, fatal error", __func__);
VOS_BUG(0);
}
}
}
vos_wait_for_work_thread_completion(__func__);
/* If unload never completes, then do kernel panic. */
hdd_unload_timer_init();
hdd_unload_timer_start(HDD_UNLOAD_WAIT_TIME);
hif_unregister_driver();
hdd_unload_timer_del();
vos_preClose( &pVosContext );
#ifdef TIMER_MANAGER
vos_timer_exit();
#endif
#ifdef MEMORY_DEBUG
adf_net_buf_debug_exit();
vos_mem_exit();
#endif
#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
wlan_logging_sock_deinit_svc();
#endif
done:
hdd_wlan_wakelock_destroy();
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,
struct kernel_param *kp)
{
return param_set_copystring(kmessage, kp);
}
#if ! defined(QCA_WIFI_FTM)
static int con_mode_handler(const char *kmessage,
struct kernel_param *kp)
{
return param_set_int(kmessage, kp);
}
#endif
#else /* #ifdef MODULE */
/**---------------------------------------------------------------------------
\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,
struct kernel_param *kp)
{
int ret;
bool mode_change;
ret = param_set_copystring(kmessage, kp);
if (!ret) {
bool ready;
ret = strncmp(fwpath_mode_local, kmessage , 3);
mode_change = ret ? true : false;
pr_info("%s : new_mode : %s, present_mode : %s\n", __func__,
kmessage, fwpath_mode_local);
strlcpy(fwpath_mode_local, kmessage,
sizeof(fwpath_mode_local));
ready = vos_is_load_unload_ready(__func__);
if (!ready) {
VOS_ASSERT(0);
return -EINVAL;
}
vos_load_unload_protect(__func__);
ret = kickstart_driver(true, mode_change);
vos_load_unload_unprotect(__func__);
}
return ret;
}
#if ! defined(QCA_WIFI_FTM)
/**---------------------------------------------------------------------------
\brief con_mode_handler() -
Handler function for module param con_mode when it is changed by user space
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, struct kernel_param *kp)
{
int ret;
ret = param_set_int(kmessage, kp);
if (0 == ret)
ret = kickstart_driver(true, false);
return ret;
}
#endif
#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)
{
#ifndef WLAN_FEATURE_MBSSID
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
#endif
VOS_STATUS vosStatus = VOS_STATUS_E_FAULT;
ENTER();
hddLog(LOG1, "hdd_softap_sta_deauth:(%pK, false)",
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
//Ignore request to deauth bcmc station
if (pDelStaParams->peerMacAddr[0] & 0x1)
return vosStatus;
#ifdef WLAN_FEATURE_MBSSID
vosStatus = WLANSAP_DeauthSta(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
pDelStaParams);
#else
vosStatus = WLANSAP_DeauthSta(pVosContext, pDelStaParams);
#endif
EXIT();
return vosStatus;
}
/**---------------------------------------------------------------------------
\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,
struct tagCsrDelStaParams *pDelStaParams)
{
#ifndef WLAN_FEATURE_MBSSID
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
#endif
ENTER();
hddLog( LOGE, "hdd_softap_sta_disassoc:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
//Ignore request to disassoc bcmc station
if( pDelStaParams->peerMacAddr[0] & 0x1 )
return;
#ifdef WLAN_FEATURE_MBSSID
WLANSAP_DisassocSta(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), pDelStaParams);
#else
WLANSAP_DisassocSta(pVosContext, pDelStaParams);
#endif
}
void hdd_softap_tkip_mic_fail_counter_measure(hdd_adapter_t *pAdapter,v_BOOL_t enable)
{
#ifndef WLAN_FEATURE_MBSSID
v_CONTEXT_t pVosContext = (WLAN_HDD_GET_CTX(pAdapter))->pvosContext;
#endif
ENTER();
hddLog( LOGE, "hdd_softap_tkip_mic_fail_counter_measure:(%pK, false)", (WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
#ifdef WLAN_FEATURE_MBSSID
WLANSAP_SetCounterMeasure(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), (v_BOOL_t)enable);
#else
WLANSAP_SetCounterMeasure(pVosContext, (v_BOOL_t)enable);
#endif
}
/**---------------------------------------------------------------------------
*
* \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)
{
hddLog(VOS_TRACE_LEVEL_INFO, "%s: concurrency_mode = 0x%x", __func__,
pHddCtx->concurrency_mode);
return (tVOS_CONCURRENCY_MODE)pHddCtx->concurrency_mode;
}
}
/* we are in an invalid state :( */
hddLog(LOGE, "%s: Invalid context", __func__);
return VOS_STA;
}
/* 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:
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]);
hdd_wlan_green_ap_start_bss(pHddCtx);
}
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:
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]);
hdd_wlan_green_ap_start_bss(pHddCtx);
}
/**---------------------------------------------------------------------------
*
* \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:
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])
pHddCtx->no_of_active_sessions[mode]--;
break;
default:
break;
}
hddLog(VOS_TRACE_LEVEL_INFO, FL("No.# of active sessions for mode %d = %d"),
mode,
pHddCtx->no_of_active_sessions[mode]);
}
/**
* wlan_hdd_get_active_session_count() - get active session
* connection count
* @hdd_ctx: Pointer to hdd context
*
* Return: count of active connections
*/
uint8_t wlan_hdd_get_active_session_count(hdd_context_t *hdd_ctx)
{
uint8_t i = 0;
uint8_t count = 0;
for (i = 0; i < VOS_MAX_NO_OF_MODE; i++) {
count += hdd_ctx->no_of_active_sessions[i];
}
return count;
}
/**
* wlan_hdd_update_txrx_chain_mask() - updates the TX/RX chain
* mask to FW
* @hdd_ctx: Pointer to hdd context
* @chain_mask : Vlaue of the chain_mask to be updated
*
* Return: 0 for success non-zero for failure
*/
int wlan_hdd_update_txrx_chain_mask(hdd_context_t *hdd_ctx,
uint8_t chain_mask)
{
int ret;
if (hdd_ctx->per_band_chainmask_supp == 1) {
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_RX_CHAIN_MASK_2G,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set 2G RX chain mask: %d"),
chain_mask);
return -EFAULT;
}
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_TX_CHAIN_MASK_2G,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set 2G TX chain mask: %d"),
chain_mask);
return -EFAULT;
}
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_RX_CHAIN_MASK_5G,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set 5G RX chain mask: %d"),
chain_mask);
return -EFAULT;
}
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_TX_CHAIN_MASK_5G,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set 5G TX chain mask: %d"),
chain_mask);
return -EFAULT;
}
} else {
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_RX_CHAIN_MASK,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set RX chain mask: %d"),
chain_mask);
return -EFAULT;
}
ret = process_wma_set_command(0,
WMI_PDEV_PARAM_TX_CHAIN_MASK,
chain_mask, PDEV_CMD);
if (0 != ret) {
hddLog(LOGE, FL("Failed to set TX chain mask: %d"),
chain_mask);
return -EFAULT;
}
}
hddLog(LOG1, FL("Sucessfully updated the TX/RX chain mask to: %d"),
chain_mask);
return 0;
}
/**---------------------------------------------------------------------------
*
* \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 management 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) {
hddLog(LOGP,
"restarting the driver(intf:\'%s\' mode:%s(%d) :try %d)",
pAdapterNode->pAdapter->dev->name,
hdd_device_mode_to_string(pAdapterNode->pAdapter->device_mode),
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)) || defined(WITH_BACKPORTS)
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_WARN,
"%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_intr();
#endif
return status;
}
/*
* 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);
}
#ifdef FEATURE_WLAN_CH_AVOID
/**
* hdd_find_prefd_safe_chnl - Finds safe channel within preferred channel
* @hdd_ctxt: hdd context pointer
* @ap_adapter: hdd hostapd adapter pointer
*
* If auto channel selection enabled:
* Preferred and safe channel should be used
* If no overlapping, preferred channel should be used
*
* Return:
* 1: found preferred safe channel
* 0: could not found preferred safe channel
*/
uint8_t hdd_find_prefd_safe_chnl(hdd_context_t *hdd_ctxt,
hdd_adapter_t *ap_adapter)
{
uint16_t safe_channels[NUM_20MHZ_RF_CHANNELS];
uint16_t safe_channel_count;
uint16_t unsafe_channel_count;
uint8_t is_unsafe = 1;
uint16_t i;
uint16_t channel_loop;
if (!hdd_ctxt || !ap_adapter) {
hddLog(LOGE, "%s : Invalid arguments: hdd_ctxt=%pK, ap_adapter=%pK",
__func__, hdd_ctxt, ap_adapter);
return 0;
}
safe_channel_count = 0;
unsafe_channel_count = VOS_MIN((uint16_t)hdd_ctxt->unsafe_channel_count,
(uint16_t)NUM_20MHZ_RF_CHANNELS);
for (i = 0; i < NUM_20MHZ_RF_CHANNELS; i++) {
is_unsafe = 0;
for (channel_loop = 0;
channel_loop < unsafe_channel_count; channel_loop++) {
if (rfChannels[i].channelNum ==
hdd_ctxt->unsafe_channel_list[channel_loop]) {
is_unsafe = 1;
break;
}
}
if (!is_unsafe) {
safe_channels[safe_channel_count] = rfChannels[i].channelNum;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"safe channel %d", safe_channels[safe_channel_count]);
safe_channel_count++;
}
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"perferred range %d - %d",
ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.start_ch,
ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.end_ch);
for (i = 0; i < safe_channel_count; i++) {
if ((safe_channels[i] >=
ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.start_ch) &&
(safe_channels[i] <=
ap_adapter->sessionCtx.ap.sapConfig.acs_cfg.end_ch)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
"safe channel %d is in perferred range", safe_channels[i]);
return 1;
}
}
return 0;
}
/**
* hdd_unsafe_channel_restart_sap - restart sap if sap is on unsafe channel
* @hdd_ctx: hdd context pointer
*
* hdd_unsafe_channel_restart_sap check all unsafe channel list
* and if ACS is enabled, driver will ask userspace to restart the
* sap. User space on LTE coex indication restart driver.
*
* Return - none
*/
void hdd_unsafe_channel_restart_sap(hdd_context_t *hdd_ctx)
{
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
eRfChannels channel_loop;
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 && (WLAN_HDD_SOFTAP == adapter->device_mode))) {
status = hdd_get_next_adapter(hdd_ctx, adapter_node,
&next);
adapter_node = next;
continue;
}
/*
* If auto channel select is enabled
* preferred channel is in safe channel,
* re-start softap interface with safe channel.
* no overlap with preferred channel and safe channel
* do not re-start softap interface
* stay current operating channel.
*/
if ((adapter->sessionCtx.ap.sapConfig.acs_cfg.acs_mode) &&
(!hdd_find_prefd_safe_chnl(hdd_ctx, adapter)))
return;
hddLog(LOG1, FL("Current operation channel %d"),
adapter->sessionCtx.ap.operatingChannel);
for (channel_loop = 0;
channel_loop < hdd_ctx->unsafe_channel_count;
channel_loop++) {
if (((hdd_ctx->unsafe_channel_list[channel_loop] ==
adapter->sessionCtx.ap.operatingChannel)) &&
(false == hdd_ctx->is_ch_avoid_in_progress) &&
(adapter->sessionCtx.ap.sapConfig.acs_cfg.
acs_mode == true)) {
hdd_change_ch_avoidance_status(hdd_ctx, true);
vos_flush_work(
&hdd_ctx->sap_start_work);
/*
* current operating channel
* is un-safe channel, restart driver
*/
hddLog(LOGE,
FL("Restarting SAP due to unsafe channel"));
/* SAP restart due to unsafe channel. While
* restarting the SAP, makee sure to clear
* acs_channel, channel to reset to 0.
* Otherwise these settings will override the
* ACS while restart.
*/
hdd_ctx->acs_policy.acs_channel =
AUTO_CHANNEL_SELECT;
adapter->sessionCtx.ap.sapConfig.channel =
AUTO_CHANNEL_SELECT;
hddLog(LOG1, FL("set sapConfig.channel to %d"),
AUTO_CHANNEL_SELECT);
wlan_hdd_send_svc_nlink_msg(
hdd_ctx->radio_index,
WLAN_SVC_LTE_COEX_IND,
NULL,
0);
hddLog(LOG1, FL("driver to start sap: %d"),
hdd_ctx->cfg_ini->sap_internal_restart);
if (hdd_ctx->cfg_ini->sap_internal_restart) {
wlan_hdd_netif_queue_control(adapter,
WLAN_NETIF_TX_DISABLE,
WLAN_CONTROL_PATH);
schedule_work(
&hdd_ctx->sap_start_work);
}
else
hdd_hostapd_stop(adapter->dev);
return;
}
}
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
return;
}
/**---------------------------------------------------------------------------
\brief hdd_ch_avoid_cb() -
Avoid channel notification from FW handler.
FW will send un-safe channel list to avoid over wrapping.
hostapd should not use notified channel
\param - pAdapter HDD adapter pointer
indParam channel avoid notification parameter
\return - None
--------------------------------------------------------------------------*/
void hdd_ch_avoid_cb
(
void *hdd_context,
void *indi_param
)
{
hdd_context_t *hdd_ctxt;
tSirChAvoidIndType *ch_avoid_indi;
v_U8_t range_loop;
eRfChannels channel_loop, start_channel_idx = INVALID_RF_CHANNEL,
end_channel_idx = INVALID_RF_CHANNEL;
v_U16_t start_channel;
v_U16_t end_channel;
v_CONTEXT_t vos_context;
tHddAvoidFreqList hdd_avoid_freq_list;
tANI_U32 i;
/* Basic sanity */
if (!hdd_context || !indi_param)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s : Invalid arguments", __func__);
return;
}
hdd_ctxt = (hdd_context_t *)hdd_context;
ch_avoid_indi = (tSirChAvoidIndType *)indi_param;
vos_context = hdd_ctxt->pvosContext;
/* Make unsafe channel list */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : band count %d",
__func__, ch_avoid_indi->avoid_range_count);
/* generate vendor specific event */
vos_mem_zero((void *)&hdd_avoid_freq_list, sizeof(tHddAvoidFreqList));
for (i = 0; i < ch_avoid_indi->avoid_range_count; i++)
{
hdd_avoid_freq_list.avoidFreqRange[i].startFreq =
ch_avoid_indi->avoid_freq_range[i].start_freq;
hdd_avoid_freq_list.avoidFreqRange[i].endFreq =
ch_avoid_indi->avoid_freq_range[i].end_freq;
}
hdd_avoid_freq_list.avoidFreqRangeCount = ch_avoid_indi->avoid_range_count;
/* clear existing unsafe channel cache */
hdd_ctxt->unsafe_channel_count = 0;
vos_mem_zero(hdd_ctxt->unsafe_channel_list,
sizeof(hdd_ctxt->unsafe_channel_list));
for (range_loop = 0; range_loop < ch_avoid_indi->avoid_range_count;
range_loop++) {
if (hdd_ctxt->unsafe_channel_count >= NUM_20MHZ_RF_CHANNELS) {
hddLog(LOGW, FL("LTE Coex unsafe channel list full"));
break;
}
start_channel = ieee80211_frequency_to_channel(
ch_avoid_indi->avoid_freq_range[range_loop].start_freq);
end_channel = ieee80211_frequency_to_channel(
ch_avoid_indi->avoid_freq_range[range_loop].end_freq);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : start %d : %d, end %d : %d", __func__,
ch_avoid_indi->avoid_freq_range[range_loop].start_freq,
start_channel,
ch_avoid_indi->avoid_freq_range[range_loop].end_freq,
end_channel);
/* do not process frequency bands that are not mapped to predefined
* channels
*/
if (start_channel == 0 || end_channel == 0)
continue;
for (channel_loop = MIN_20MHZ_RF_CHANNEL; channel_loop <=
MAX_20MHZ_RF_CHANNEL; channel_loop++) {
if (rfChannels[channel_loop].targetFreq >=
ch_avoid_indi->avoid_freq_range[range_loop].start_freq) {
start_channel_idx = channel_loop;
break;
}
}
for (channel_loop = MIN_20MHZ_RF_CHANNEL; channel_loop <=
MAX_20MHZ_RF_CHANNEL; channel_loop++) {
if (rfChannels[channel_loop].targetFreq >=
ch_avoid_indi->avoid_freq_range[range_loop].end_freq) {
end_channel_idx = channel_loop;
if (rfChannels[channel_loop].targetFreq >
ch_avoid_indi->avoid_freq_range[range_loop].end_freq)
end_channel_idx--;
break;
}
}
if (start_channel_idx == INVALID_RF_CHANNEL ||
end_channel_idx == INVALID_RF_CHANNEL)
continue;
for (channel_loop = start_channel_idx; channel_loop <=
end_channel_idx; channel_loop++) {
hdd_ctxt->unsafe_channel_list[hdd_ctxt->unsafe_channel_count++]
= rfChannels[channel_loop].channelNum;
if (hdd_ctxt->unsafe_channel_count >= NUM_20MHZ_RF_CHANNELS) {
hddLog(LOGW, FL("LTE Coex unsafe channel list full"));
break;
}
}
}
#ifdef CONFIG_CNSS
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : number of unsafe channels is %d ",
__func__, hdd_ctxt->unsafe_channel_count);
if (vos_set_wlan_unsafe_channel(hdd_ctxt->unsafe_channel_list,
hdd_ctxt->unsafe_channel_count)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Failed to set unsafe channel",
__func__);
/* clear existing unsafe channel cache */
hdd_ctxt->unsafe_channel_count = 0;
vos_mem_zero(hdd_ctxt->unsafe_channel_list,
sizeof(v_U16_t) * NUM_20MHZ_RF_CHANNELS);
return;
}
for (channel_loop = 0;
channel_loop < hdd_ctxt->unsafe_channel_count;
channel_loop++)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: channel %d is not safe ", __func__,
hdd_ctxt->unsafe_channel_list[channel_loop]);
}
#endif
/*
* first update the unsafe channel list to the platform driver and
* send the avoid freq event to the application
*/
wlan_hdd_send_avoid_freq_event(hdd_ctxt, &hdd_avoid_freq_list);
if (0 == hdd_ctxt->unsafe_channel_count)
return;
hdd_unsafe_channel_restart_sap(hdd_ctxt);
return;
}
#endif /* FEATURE_WLAN_CH_AVOID */
#ifdef WLAN_FEATURE_LPSS
int wlan_hdd_gen_wlan_status_pack(struct wlan_status_data *data,
hdd_adapter_t *pAdapter,
hdd_station_ctx_t *pHddStaCtx,
v_U8_t is_on,
v_U8_t is_connected)
{
hdd_context_t *pHddCtx = NULL;
tANI_U8 buflen = WLAN_SVC_COUNTRY_CODE_LEN;
if (!data) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: invalid data pointer", __func__);
return (-1);
}
if (!pAdapter) {
if (is_on) {
/* no active interface */
data->lpss_support = 0;
data->is_on = is_on;
return 0;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: invalid adapter pointer", __func__);
return (-1);
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (pHddCtx->lpss_support && pHddCtx->cfg_ini->enablelpasssupport)
data->lpss_support = 1;
else
data->lpss_support = 0;
data->numChannels = WLAN_SVC_MAX_NUM_CHAN;
sme_GetCfgValidChannels(pHddCtx->hHal, data->channel_list,
&data->numChannels);
sme_GetCountryCode(pHddCtx->hHal, data->country_code, &buflen);
data->is_on = is_on;
data->vdev_id = pAdapter->sessionId;
data->vdev_mode = pAdapter->device_mode;
if (pHddStaCtx) {
data->is_connected = is_connected;
data->rssi = pAdapter->rssi;
data->freq = vos_chan_to_freq(pHddStaCtx->conn_info.operationChannel);
if (WLAN_SVC_MAX_SSID_LEN >= pHddStaCtx->conn_info.SSID.SSID.length) {
data->ssid_len = pHddStaCtx->conn_info.SSID.SSID.length;
memcpy(data->ssid,
pHddStaCtx->conn_info.SSID.SSID.ssId,
pHddStaCtx->conn_info.SSID.SSID.length);
}
if (WLAN_SVC_MAX_BSSID_LEN >= sizeof(pHddStaCtx->conn_info.bssId))
memcpy(data->bssid,
pHddStaCtx->conn_info.bssId,
sizeof(pHddStaCtx->conn_info.bssId));
}
return 0;
}
int wlan_hdd_gen_wlan_version_pack(struct wlan_version_data *data,
v_U32_t fw_version,
v_U32_t chip_id,
const char *chip_name)
{
if (!data) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: invalid data pointer", __func__);
return (-1);
}
data->chip_id = chip_id;
strlcpy(data->chip_name, chip_name, WLAN_SVC_MAX_STR_LEN);
if (strncmp(chip_name, "Unknown", 7))
strlcpy(data->chip_from, "Qualcomm", WLAN_SVC_MAX_STR_LEN);
else
strlcpy(data->chip_from, "Unknown", WLAN_SVC_MAX_STR_LEN);
strlcpy(data->host_version, QWLAN_VERSIONSTR, WLAN_SVC_MAX_STR_LEN);
scnprintf(data->fw_version, WLAN_SVC_MAX_STR_LEN, "%d.%d.%d.%d",
(fw_version & 0xf0000000) >> 28,
(fw_version & 0xf000000) >> 24,
(fw_version & 0xf00000) >> 20,
(fw_version & 0x7fff));
return 0;
}
#endif
#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
/**---------------------------------------------------------------------------
\brief wlan_hdd_disable_roaming()
This function loop through each adapter and disable roaming on each STA
device mode except the input adapter.
Note: On the input adapter roaming is not enabled yet hence no need to
disable.
\param - pAdapter HDD adapter pointer
\return - None
--------------------------------------------------------------------------*/
void wlan_hdd_disable_roaming(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_adapter_t *pAdapterIdx = NULL;
hdd_adapter_list_node_t *pAdapterNode = NULL;
hdd_adapter_list_node_t *pNext = NULL;
VOS_STATUS status;
if (pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled &&
pHddCtx->cfg_ini->isRoamOffloadScanEnabled &&
WLAN_HDD_INFRA_STATION == pAdapter->device_mode &&
vos_is_sta_active_connection_exists()) {
hddLog(LOG1, FL("Connect received on STA sessionId(%d)"),
pAdapter->sessionId);
/* Loop through adapter and disable roaming for each STA device mode
except the input adapter. */
status = hdd_get_front_adapter (pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pAdapterIdx = pAdapterNode->pAdapter;
if (WLAN_HDD_INFRA_STATION == pAdapterIdx->device_mode &&
pAdapter->sessionId != pAdapterIdx->sessionId) {
hddLog(LOG1, FL("Disable Roaming on sessionId(%d)"),
pAdapterIdx->sessionId);
sme_stopRoaming(WLAN_HDD_GET_HAL_CTX(pAdapterIdx),
pAdapterIdx->sessionId, 0);
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
}
}
/**---------------------------------------------------------------------------
\brief wlan_hdd_enable_roaming()
This function loop through each adapter and enable roaming on each STA
device mode except the input adapter.
Note: On the input adapter no need to enable roaming because link got
disconnected on this.
\param - pAdapter HDD adapter pointer
\return - None
--------------------------------------------------------------------------*/
void wlan_hdd_enable_roaming(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_adapter_t *pAdapterIdx = NULL;
hdd_adapter_list_node_t *pAdapterNode = NULL;
hdd_adapter_list_node_t *pNext = NULL;
VOS_STATUS status;
if (pHddCtx->cfg_ini->isFastRoamIniFeatureEnabled &&
pHddCtx->cfg_ini->isRoamOffloadScanEnabled &&
WLAN_HDD_INFRA_STATION == pAdapter->device_mode &&
vos_is_sta_active_connection_exists()) {
hddLog(LOG1, FL("Disconnect received on STA sessionId(%d)"),
pAdapter->sessionId);
/* Loop through adapter and enable roaming for each STA device mode
except the input adapter. */
status = hdd_get_front_adapter (pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pAdapterIdx = pAdapterNode->pAdapter;
if (WLAN_HDD_INFRA_STATION == pAdapterIdx->device_mode &&
pAdapter->sessionId != pAdapterIdx->sessionId) {
hddLog(LOG1, FL("Enabling Roaming on sessionId(%d)"),
pAdapterIdx->sessionId);
sme_startRoaming(WLAN_HDD_GET_HAL_CTX(pAdapterIdx),
pAdapterIdx->sessionId,
REASON_CONNECT);
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
}
}
#endif
/**
* nl_srv_bcast_svc() - Wrapper function to send bcast msgs to SVC mcast group
* @skb: sk buffer pointer
*
* Sends the bcast message to SVC multicast group with generic nl socket
* if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
*
* Return: None
*/
static void nl_srv_bcast_svc(struct sk_buff *skb)
{
#ifdef CNSS_GENL
nl_srv_bcast(skb, CLD80211_MCGRP_SVC_MSGS, WLAN_NL_MSG_SVC);
#else
nl_srv_bcast(skb);
#endif
}
void wlan_hdd_send_svc_nlink_msg(int radio, int type, void *data, int len)
{
struct sk_buff *skb;
struct nlmsghdr *nlh;
tAniMsgHdr *ani_hdr;
void *nl_data = NULL;
int flags = GFP_KERNEL;
struct radio_index_tlv *radio_info;
int tlv_len;
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_FW_CRASHED_IND:
case WLAN_SVC_FW_SHUTDOWN_IND:
case WLAN_SVC_LTE_COEX_IND:
case WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND:
case WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND:
ani_hdr->length = 0;
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
break;
case WLAN_SVC_WLAN_STATUS_IND:
case WLAN_SVC_WLAN_VERSION_IND:
case WLAN_SVC_DFS_CAC_START_IND:
case WLAN_SVC_DFS_CAC_END_IND:
case WLAN_SVC_DFS_RADAR_DETECT_IND:
case WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND:
case WLAN_SVC_WLAN_TP_IND:
case WLAN_SVC_WLAN_TP_TX_IND:
case WLAN_SVC_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);
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"WLAN SVC: Attempt to send unknown nlink message %d", type);
kfree_skb(skb);
return;
}
/*
* Add radio index at the end of the svc event in TLV format to maintain
* the backward compatibilty with userspace applications.
*/
tlv_len = 0;
if ((sizeof(*ani_hdr) + len + sizeof(struct radio_index_tlv))
< WLAN_NL_MAX_PAYLOAD) {
radio_info = (struct radio_index_tlv *)((char *) ani_hdr +
sizeof (*ani_hdr) + len);
radio_info->type = (unsigned short) WLAN_SVC_WLAN_RADIO_INDEX;
radio_info->length = (unsigned short) sizeof(radio_info->radio);
radio_info->radio = radio;
tlv_len = sizeof(*radio_info);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Added radio index tlv - radio index %d", radio_info->radio);
}
nlh->nlmsg_len += tlv_len;
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len + tlv_len));
nl_srv_bcast_svc(skb);
return;
}
#ifdef WLAN_FEATURE_LPSS
void wlan_hdd_send_status_pkg(hdd_adapter_t *pAdapter,
hdd_station_ctx_t *pHddStaCtx,
v_U8_t is_on,
v_U8_t is_connected)
{
int ret = 0;
struct wlan_status_data data;
hdd_context_t *hdd_ctx;
v_PVOID_t vos_ctx;
if (VOS_FTM_MODE == hdd_get_conparam())
return;
memset(&data, 0, sizeof(struct wlan_status_data));
if (is_on)
ret = wlan_hdd_gen_wlan_status_pack(&data, pAdapter, pHddStaCtx,
is_on, is_connected);
if (!ret) {
if (pAdapter) {
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
} else {
vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
}
if (!hdd_ctx)
return;
wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
WLAN_SVC_WLAN_STATUS_IND,
&data, sizeof(struct wlan_status_data));
}
}
void wlan_hdd_send_version_pkg(v_U32_t fw_version,
v_U32_t chip_id,
const char *chip_name)
{
int ret = 0;
struct wlan_version_data data;
v_PVOID_t vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
hdd_context_t *hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
if (VOS_FTM_MODE == hdd_get_conparam())
return;
if (!hdd_ctx)
return;
memset(&data, 0, sizeof(struct wlan_version_data));
ret = wlan_hdd_gen_wlan_version_pack(&data, fw_version, chip_id,
chip_name);
if (!ret)
wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
WLAN_SVC_WLAN_VERSION_IND,
&data, sizeof(struct wlan_version_data));
}
void wlan_hdd_send_all_scan_intf_info(hdd_context_t *pHddCtx)
{
hdd_adapter_t *pDataAdapter = NULL;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
v_BOOL_t scan_intf_found = VOS_FALSE;
VOS_STATUS status;
if (!pHddCtx) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: NULL pointer for pHddCtx",
__func__);
return;
}
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pDataAdapter = pAdapterNode->pAdapter;
if (pDataAdapter) {
if (pDataAdapter->device_mode == WLAN_HDD_INFRA_STATION ||
pDataAdapter->device_mode == WLAN_HDD_P2P_CLIENT ||
pDataAdapter->device_mode == WLAN_HDD_P2P_DEVICE) {
scan_intf_found = VOS_TRUE;
wlan_hdd_send_status_pkg(pDataAdapter, NULL, 1, 0);
}
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
if (!scan_intf_found)
wlan_hdd_send_status_pkg(pDataAdapter, NULL, 1, 0);
}
#endif
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
v_VOID_t wlan_hdd_auto_shutdown_cb(v_VOID_t)
{
v_PVOID_t vos_ctx = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
hdd_context_t *hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx);
if (!hdd_ctx)
return;
hddLog(LOGE, FL("%s: Wlan Idle. Sending Shutdown event.."),__func__);
wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
WLAN_SVC_WLAN_AUTO_SHUTDOWN_IND, NULL, 0);
}
void wlan_hdd_auto_shutdown_enable(hdd_context_t *hdd_ctx, v_BOOL_t enable)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
v_BOOL_t ap_connected = VOS_FALSE, sta_connected = VOS_FALSE;
tHalHandle hHal;
hHal = hdd_ctx->hHal;
if (hHal == NULL)
return;
if (hdd_ctx->cfg_ini->WlanAutoShutdown == 0)
return;
if (enable == VOS_FALSE) {
if (sme_set_auto_shutdown_timer(hHal, 0) != eHAL_STATUS_SUCCESS) {
hddLog(LOGE, FL("Failed to stop wlan auto shutdown timer"));
}
wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
WLAN_SVC_WLAN_AUTO_SHUTDOWN_CANCEL_IND, NULL, 0);
return;
}
/* To enable shutdown timer check concurrency */
if (vos_concurrent_open_sessions_running()) {
status = hdd_get_front_adapter ( hdd_ctx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) {
pAdapter = pAdapterNode->pAdapter;
if (pAdapter && pAdapter->device_mode == WLAN_HDD_INFRA_STATION) {
if (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState
== eConnectionState_Associated) {
sta_connected = VOS_TRUE;
break;
}
}
if (pAdapter && pAdapter->device_mode == WLAN_HDD_SOFTAP) {
if(WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_TRUE) {
ap_connected = VOS_TRUE;
break;
}
}
status = hdd_get_next_adapter ( hdd_ctx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
if (ap_connected == VOS_TRUE || sta_connected == VOS_TRUE) {
hddLog(LOG1, FL("CC Session active. Shutdown timer not enabled"));
return;
} else {
if (sme_set_auto_shutdown_timer(hHal,
hdd_ctx->cfg_ini->WlanAutoShutdown)
!= eHAL_STATUS_SUCCESS)
hddLog(LOGE, FL("Failed to start wlan auto shutdown timer"));
else
hddLog(LOG1, FL("Auto Shutdown timer for %d seconds enabled"),
hdd_ctx->cfg_ini->WlanAutoShutdown);
}
}
#endif
hdd_adapter_t * hdd_get_con_sap_adapter(hdd_adapter_t *this_sap_adapter,
bool check_start_bss)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(this_sap_adapter);
hdd_adapter_t *pAdapter, *con_sap_adapter;
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
con_sap_adapter = NULL;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) {
pAdapter = pAdapterNode->pAdapter;
if (pAdapter && ((pAdapter->device_mode == WLAN_HDD_SOFTAP) ||
(pAdapter->device_mode == WLAN_HDD_P2P_GO)) &&
pAdapter != this_sap_adapter) {
if (check_start_bss) {
if (test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)) {
con_sap_adapter = pAdapter;
break;
}
} else {
con_sap_adapter = pAdapter;
break;
}
}
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return con_sap_adapter;
}
#ifdef FEATURE_BUS_BANDWIDTH
void hdd_start_bus_bw_compute_timer(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->bus_bw_timer))
return;
vos_timer_start(&pHddCtx->bus_bw_timer,
pHddCtx->cfg_ini->busBandwidthComputeInterval);
}
void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *pAdapter)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
v_BOOL_t can_stop = VOS_TRUE;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (VOS_TIMER_STATE_RUNNING !=
vos_timer_getCurrentState(&pHddCtx->bus_bw_timer)) {
/* trying to stop timer, when not running is not good */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"bus band width compute timer is not running");
return;
}
if (vos_concurrent_open_sessions_running()) {
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status ) {
pAdapter = pAdapterNode->pAdapter;
if (pAdapter && (pAdapter->device_mode == WLAN_HDD_INFRA_STATION ||
pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) &&
WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)->conn_info.connState
== eConnectionState_Associated) {
can_stop = VOS_FALSE;
break;
}
if (pAdapter && (pAdapter->device_mode == WLAN_HDD_SOFTAP ||
pAdapter->device_mode == WLAN_HDD_P2P_GO) &&
WLAN_HDD_GET_AP_CTX_PTR(pAdapter)->bApActive == VOS_TRUE) {
can_stop = VOS_FALSE;
break;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
}
if (can_stop == VOS_TRUE) {
vos_timer_stop(&pHddCtx->bus_bw_timer);
if (pHddCtx->hbw_requested) {
vos_remove_pm_qos();
pHddCtx->hbw_requested = false;
}
/* reset the ipa perf level */
hdd_ipa_set_perf_level(pHddCtx, 0, 0);
hdd_rst_tcp_delack(pHddCtx);
tlshim_reset_bundle_require();
tlshim_driver_del_ack_disable();
}
}
#endif
#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
void wlan_hdd_check_sta_ap_concurrent_ch_intf(void *data)
{
hdd_adapter_t *ap_adapter = NULL, *sta_adapter = (hdd_adapter_t *)data;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(sta_adapter);
tHalHandle hHal;
hdd_ap_ctx_t *pHddApCtx;
uint16_t intf_ch = 0, vht_channel_width = 0;
eCsrBand orig_band, new_band;
if ((pHddCtx->cfg_ini->WlanMccToSccSwitchMode == VOS_MCC_TO_SCC_SWITCH_DISABLE)
|| !(vos_concurrent_open_sessions_running()
|| !(vos_get_concurrency_mode() == VOS_STA_SAP)))
return;
ap_adapter = hdd_get_adapter(pHddCtx, WLAN_HDD_SOFTAP);
if (ap_adapter == NULL)
return;
if (!test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags))
return;
pHddApCtx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
hHal = WLAN_HDD_GET_HAL_CTX(ap_adapter);
if (hHal == NULL)
return;
intf_ch = WLANSAP_CheckCCIntf(pHddApCtx->sapContext);
vht_channel_width = wlan_sap_get_vht_ch_width(pHddApCtx->sapContext);
if (intf_ch == 0)
return;
if (pHddApCtx->sapConfig.band_switch_enable) {
if (pHddApCtx->sapConfig.channel > MAX_2_4GHZ_CHANNEL) {
orig_band = eCSR_BAND_5G;
} else {
orig_band = eCSR_BAND_24;
}
if (intf_ch > MAX_2_4GHZ_CHANNEL) {
new_band = eCSR_BAND_5G;
} else {
new_band = eCSR_BAND_24;
}
if (orig_band != new_band) {
if (new_band == eCSR_BAND_5G) {
pHddApCtx->sapConfig.ch_width_orig =
pHddApCtx->sapConfig.ch_width_5g_orig;
} else {
pHddApCtx->sapConfig.ch_width_orig =
pHddApCtx->sapConfig.ch_width_24g_orig;
}
}
}
hddLog(VOS_TRACE_LEVEL_INFO,
FL("SAP restarts due to MCC->SCC switch, orig chan: %d, new chan: %d"),
pHddApCtx->sapConfig.channel, intf_ch);
pHddApCtx->sapConfig.channel = intf_ch;
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
if(vos_is_ch_switch_with_csa_enabled())
{
struct wlan_sap_csa_info csa_info;
csa_info.sta_channel = intf_ch;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH,
"%s: Indicate connected event to HostApd Chan=%d",
__func__, csa_info.sta_channel);
/*Indicate to HostApd about Station interface state change*/
hdd_sta_state_sap_notify(pHddCtx, STA_NOTIFY_CONNECTED, csa_info);
}else{
#endif
pHddApCtx->bss_stop_reason = BSS_STOP_DUE_TO_MCC_SCC_SWITCH;
sme_SelectCBMode(hHal,
pHddApCtx->sapConfig.SapHw_mode,
pHddApCtx->sapConfig.channel,
pHddApCtx->sapConfig.sec_ch,
&vht_channel_width, pHddApCtx->sapConfig.ch_width_orig);
wlan_sap_set_vht_ch_width(pHddApCtx->sapContext, vht_channel_width);
wlan_hdd_restart_sap(ap_adapter);
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
}
#endif
}
#endif
#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
void hdd_csa_notify_cb
(
void *hdd_context,
void *indi_param
)
{
tpSmeCsaOffloadInd csa_params = NULL;
hdd_context_t *hdd_ctxt = NULL;
struct wlan_sap_csa_info csa_info;
v_U32_t ret = 0;
/* Basic sanity */
if(!vos_is_ch_switch_with_csa_enabled())
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s : SAP channel switch with CSA not enabled", __func__);
return;
}
if (!hdd_context || !indi_param)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s : Invalid arguments", __func__);
return;
}
hdd_ctxt = (hdd_context_t *)hdd_context;
csa_params = (tpSmeCsaOffloadInd)indi_param;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s : tbtt count %d Channel = %d",
__func__,csa_params->tbtt_count, csa_params->channel);
hdd_ctxt->ch_switch_ctx.tbtt_count = csa_params->tbtt_count - 1; /* Will reduce the count by 1,
as the switch might take time.
Currently of No Use, as this will be
override by ini g_sap_chanswitch_beacon_cnt */
csa_info.sta_channel = csa_params->channel;
ret = hdd_sta_state_sap_notify(hdd_ctxt, STA_NOTIFY_CSA, csa_info);
if(ret != 0)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s : Failed to trigger Channel Switch Ch:%d ret=%d",
__func__,csa_params->channel, ret);
}
}
#endif//#ifdef WLAN_FEATURE_SAP_TO_FOLLOW_STA_CHAN
/**
* wlan_hdd_check_custom_con_channel_rules() - This function checks the sap's
* and sta's operating channel.
* @sta_adapter: Describe the first argument to foobar.
* @ap_adapter: Describe the second argument to foobar.
* @roam_profile: Roam profile of AP to which STA wants to connect.
* @concurrent_chnl_same: If both SAP and STA channels are same then
* set this flag to true else false.
*
* This function checks the sap's operating channel and sta's operating channel.
* if both are same then it will return false else it will restart the sap in
* sta's channel and return true.
*
*
* Return: VOS_STATUS_SUCCESS or VOS_STATUS_E_FAILURE.
*/
VOS_STATUS wlan_hdd_check_custom_con_channel_rules(hdd_adapter_t *sta_adapter,
hdd_adapter_t *ap_adapter,
tCsrRoamProfile *roam_profile,
tScanResultHandle *scan_cache,
bool *concurrent_chnl_same)
{
hdd_ap_ctx_t *hdd_ap_ctx;
uint8_t channel_id;
VOS_STATUS status;
device_mode_t device_mode = ap_adapter->device_mode;
*concurrent_chnl_same = true;
hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
status =
sme_get_ap_channel_from_scan_cache(WLAN_HDD_GET_HAL_CTX(sta_adapter),
roam_profile,
scan_cache,
&channel_id);
if ((VOS_STATUS_SUCCESS == status)) {
if ((WLAN_HDD_SOFTAP == device_mode) &&
(channel_id < SIR_11A_CHANNEL_BEGIN)) {
if (hdd_ap_ctx->operatingChannel != channel_id) {
*concurrent_chnl_same = false;
hddLog(VOS_TRACE_LEVEL_INFO_MED,
FL("channels are different"));
}
} else if ((WLAN_HDD_P2P_GO == device_mode) &&
(channel_id >= SIR_11A_CHANNEL_BEGIN)) {
if (hdd_ap_ctx->operatingChannel != channel_id) {
*concurrent_chnl_same = false;
hddLog(VOS_TRACE_LEVEL_INFO_MED,
FL("channels are different"));
}
}
} else {
/*
* Lets handle worst case scenario here, Scan cache lookup is failed
* so we have to stop the SAP to avoid any channel discrepancy between
* SAP's channel and STA's channel. Return the status as failure so
* caller function could know that scan look up is failed.
*/
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Finding AP from scan cache failed"));
return VOS_STATUS_E_FAILURE;
}
return VOS_STATUS_SUCCESS;
}
#ifdef WLAN_FEATURE_MBSSID
/**
* wlan_hdd_stop_sap() - This function stops bss of SAP.
* @ap_adapter: SAP adapter
*
* This function will process the stopping of sap adapter.
*
* Return: void.
*/
void wlan_hdd_stop_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;
#ifdef CFG80211_DEL_STA_V2
struct station_del_parameters delStaParams;
#endif
if (NULL == ap_adapter) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("ap_adapter is NULL here"));
return;
}
hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
hdd_ctx = WLAN_HDD_GET_CTX(ap_adapter);
if (0 != wlan_hdd_validate_context(hdd_ctx))
return;
mutex_lock(&hdd_ctx->sap_lock);
if (test_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags)) {
#ifdef 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(hdd_ctx, ap_adapter);
hostapd_state = WLAN_HDD_GET_HOSTAP_STATE_PTR(ap_adapter);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
FL("Now doing SAP STOPBSS"));
if (VOS_STATUS_SUCCESS == WLANSAP_StopBss(hdd_ap_ctx->sapContext)) {
vos_status = vos_wait_single_event(&hostapd_state->stop_bss_event,
10000);
if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
mutex_unlock(&hdd_ctx->sap_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("SAP Stop Failed"));
return;
}
}
clear_bit(SOFTAP_BSS_STARTED, &ap_adapter->event_flags);
wlan_hdd_decr_active_session(hdd_ctx, ap_adapter->device_mode);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
FL("SAP Stop Success"));
if (hdd_ctx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_allow_suspend(hdd_ctx->runtime_context.obss);
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Can't stop ap because its not started"));
}
mutex_unlock(&hdd_ctx->sap_lock);
return;
}
/**
* 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, bool reinit)
{
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;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("ssr in progress %d"), reinit);
if (!reinit) {
if (0 != wlan_hdd_validate_context(hdd_ctx))
return;
}
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"));
WLANSAP_ResetSapConfigAddIE(pConfig, eUPDATE_IE_ALL);
goto end;
}
vos_event_reset(&hostapd_state->vosEvent);
if (WLANSAP_StartBss(hdd_ap_ctx->sapContext, 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;
if (hdd_ctx->cfg_ini->apOBSSProtEnabled)
vos_runtime_pm_prevent_suspend(hdd_ctx->runtime_context.obss);
end:
mutex_unlock(&hdd_ctx->sap_lock);
return;
}
#endif
/**
* hdd_wlan_go_set_mcc_p2p_quota() - Function to set quota for P2P GO
* @hostapd_adapter: Pointer to HDD adapter
* @set_value: Qouta value for the interface
*
* This function is used to set the quota for P2P GO cases
*
* Return: Configuration message posting status, SUCCESS or Fail
*
*/
int32_t hdd_wlan_go_set_mcc_p2p_quota(hdd_adapter_t *hostapd_adapter,
uint32_t set_value)
{
uint8_t first_adapter_operating_channel = 0;
uint8_t second_adapter_opertaing_channel = 0;
tVOS_CONCURRENCY_MODE concurrent_state = 0;
hdd_adapter_t *staAdapter = NULL;
int32_t ret = 0; /* success */
/*
* Check if concurrency mode is active.
* Need to modify this code to support MCC modes other than
* STA/P2P GO
*/
concurrent_state = hdd_get_concurrency_mode();
if (concurrent_state == (VOS_STA | VOS_P2P_GO)) {
hddLog(LOG1, "%s: STA & P2P are both enabled", __func__);
/*
* The channel numbers for both adapters and the time
* quota for the 1st adapter, i.e., one specified in cmd
* are formatted as a bit vector then passed on to WMA
* +************************************************+
* |bit 31-24 |bit 23-16 | bits 15-8 |bits 7-0 |
* | Unused | Quota for| chan. # for |chan. # for|
* | | 1st chan.| 1st chan. |2nd chan. |
* +************************************************+
*/
/* Get the operating channel of the specified vdev */
first_adapter_operating_channel =
hdd_get_operating_channel(hostapd_adapter->pHddCtx,
hostapd_adapter->device_mode);
hddLog(LOG1, "%s: 1st channel No.:%d and quota:%dms",
__func__, first_adapter_operating_channel,
set_value);
/* Move the time quota for first adapter to bits 15-8 */
set_value = set_value << 8;
/*
* Store the operating channel number of 1st adapter at
* the lower 8-bits of bit vector.
*/
set_value = set_value | first_adapter_operating_channel;
if (hostapd_adapter->device_mode ==
WLAN_HDD_INFRA_STATION) {
/* iwpriv cmd issued on wlan0; get p2p0 vdev chan */
if ((concurrent_state & VOS_P2P_CLIENT) != 0) {
/* The 2nd MCC vdev is P2P client */
staAdapter = hdd_get_adapter
(
hostapd_adapter->pHddCtx,
WLAN_HDD_P2P_CLIENT
);
} else {
/* The 2nd MCC vdev is P2P GO */
staAdapter = hdd_get_adapter
(
hostapd_adapter->pHddCtx,
WLAN_HDD_P2P_GO
);
}
} else {
/* iwpriv cmd issued on p2p0; get channel for wlan0 */
staAdapter = hdd_get_adapter
(
hostapd_adapter->pHddCtx,
WLAN_HDD_INFRA_STATION
);
}
if (staAdapter != NULL) {
second_adapter_opertaing_channel =
hdd_get_operating_channel
(
staAdapter->pHddCtx,
staAdapter->device_mode
);
hddLog(LOG1, "%s: 2nd vdev channel No. is:%d",
__func__,
second_adapter_opertaing_channel);
if (second_adapter_opertaing_channel == 0 ||
first_adapter_operating_channel == 0) {
hddLog(LOGE, "Invalid channel");
return -EINVAL;
}
/*
* Move the time quota and operating channel number
* for the first adapter to bits 23-16 & bits 15-8
* of set_value vector, respectively.
*/
set_value = set_value << 8;
/*
* Store the channel number for 2nd MCC vdev at bits
* 7-0 of set_value vector as per the bit format above.
*/
set_value = set_value |
second_adapter_opertaing_channel;
ret = process_wma_set_command
(
(int32_t)hostapd_adapter->sessionId,
(int32_t)WMA_VDEV_MCC_SET_TIME_QUOTA,
set_value,
VDEV_CMD
);
} else {
hddLog(LOGE, "%s: NULL adapter handle. Exit",
__func__);
}
} else {
hddLog(LOG1, "%s: MCC is not active. "
"Exit w/o setting latency", __func__);
}
return ret;
}
/**
* hdd_wlan_set_mcc_p2p_quota() - Function to set quota for P2P
* @hostapd_adapter: Pointer to HDD adapter
* @set_value: Qouta value for the interface
*
* This function is used to set the quota for P2P cases
*
* Return: Configuration message posting status, SUCCESS or Fail
*
*/
int32_t hdd_wlan_set_mcc_p2p_quota(hdd_adapter_t *hostapd_adapater,
uint32_t set_value)
{
uint8_t first_adapter_operating_channel = 0;
uint8_t second_adapter_opertaing_channel = 0;
hdd_adapter_t *staAdapter = NULL;
int32_t ret = 0; /* success */
tVOS_CONCURRENCY_MODE concurrent_state = hdd_get_concurrency_mode();
hddLog(LOG1, "iwpriv cmd to set MCC quota with val %dms",
set_value);
/*
* Check if concurrency mode is active.
* Need to modify this code to support MCC modes other than STA/P2P
*/
if ((concurrent_state == (VOS_STA | VOS_P2P_CLIENT)) ||
(concurrent_state == (VOS_STA | VOS_P2P_GO))) {
hddLog(LOG1, "STA & P2P are both enabled");
/*
* The channel numbers for both adapters and the time
* quota for the 1st adapter, i.e., one specified in cmd
* are formatted as a bit vector then passed on to WMA
* +***********************************************************+
* |bit 31-24 | bit 23-16 | bits 15-8 | bits 7-0 |
* | Unused | Quota for | chan. # for | chan. # for |
* | | 1st chan. | 1st chan. | 2nd chan. |
* +***********************************************************+
*/
/* Get the operating channel of the specified vdev */
first_adapter_operating_channel =
hdd_get_operating_channel
(
hostapd_adapater->pHddCtx,
hostapd_adapater->device_mode
);
hddLog(LOG1, "1st channel No.:%d and quota:%dms",
first_adapter_operating_channel, set_value);
/* Move the time quota for first channel to bits 15-8 */
set_value = set_value << 8;
/*
* Store the channel number of 1st channel at bits 7-0
* of the bit vector
*/
set_value = set_value | first_adapter_operating_channel;
/* Find out the 2nd MCC adapter and its operating channel */
if (hostapd_adapater->device_mode == WLAN_HDD_INFRA_STATION) {
/*
* iwpriv cmd was issued on wlan0;
* get p2p0 vdev channel
*/
if ((concurrent_state & VOS_P2P_CLIENT) != 0) {
/* The 2nd MCC vdev is P2P client */
staAdapter = hdd_get_adapter(
hostapd_adapater->pHddCtx,
WLAN_HDD_P2P_CLIENT);
} else {
/* The 2nd MCC vdev is P2P GO */
staAdapter = hdd_get_adapter(
hostapd_adapater->pHddCtx,
WLAN_HDD_P2P_GO);
}
} else {
/*
* iwpriv cmd was issued on p2p0;
* get wlan0 vdev channel
*/
staAdapter = hdd_get_adapter(hostapd_adapater->pHddCtx,
WLAN_HDD_INFRA_STATION);
}
if (staAdapter != NULL) {
second_adapter_opertaing_channel =
hdd_get_operating_channel
(
staAdapter->pHddCtx,
staAdapter->device_mode
);
hddLog(LOG1, "2nd vdev channel No. is:%d",
second_adapter_opertaing_channel);
if (second_adapter_opertaing_channel == 0 ||
first_adapter_operating_channel == 0) {
hddLog(LOGE, "Invalid channel");
return -EINVAL;
}
/*
* Now move the time quota and channel number of the
* 1st adapter to bits 23-16 and bits 15-8 of the bit
* vector, respectively.
*/
set_value = set_value << 8;
/*
* Store the channel number for 2nd MCC vdev at bits
* 7-0 of set_value
*/
set_value = set_value |
second_adapter_opertaing_channel;
ret = process_wma_set_command(
(int32_t)hostapd_adapater->sessionId,
(int32_t)WMA_VDEV_MCC_SET_TIME_QUOTA,
set_value, VDEV_CMD);
} else {
hddLog(LOGE, "NULL adapter handle. Exit");
}
} else {
hddLog(LOG1, "%s: MCC is not active. Exit w/o setting latency",
__func__);
}
return ret;
}
/**
* hdd_get_fw_version() - Get FW version
* @hdd_ctx: pointer to HDD context.
* @major_spid: FW version - major spid.
* @minor_spid: FW version - minor spid
* @ssid: FW version - ssid
* @crmid: FW version - crmid
*
* This function is called to get the firmware build version stored
* as part of the HDD context
*
* Return: None
*/
void hdd_get_fw_version(hdd_context_t *hdd_ctx,
uint32_t *major_spid, uint32_t *minor_spid,
uint32_t *siid, uint32_t *crmid)
{
*major_spid = (hdd_ctx->target_fw_version & 0xf0000000) >> 28;
*minor_spid = (hdd_ctx->target_fw_version & 0xf000000) >> 24;
*siid = (hdd_ctx->target_fw_version & 0xf00000) >> 20;
*crmid = hdd_ctx->target_fw_version & 0x7fff;
}
#ifdef QCA_CONFIG_SMP
int wlan_hdd_get_cpu()
{
int cpu_index = get_cpu();
put_cpu();
return cpu_index;
}
#endif
/**
* hdd_get_fwpath() - get framework path
*
* This function is used to get the string written by
* userspace to start the wlan driver
*
* Return: string
*/
const char *hdd_get_fwpath(void)
{
return fwpath.string;
}
/**
* hdd_enable_disable_ca_event() - enable/disable channel avoidance event
* @hddctx: pointer to hdd context
* @set_value: enable/disable
*
* When Host sends vendor command enable, FW will send *ONE* CA ind to
* Host(even though it is duplicate). When Host send vendor command
* 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.
*
* return - 0 on success, appropriate error values on failure.
*/
int hdd_enable_disable_ca_event(hdd_context_t *hddctx, tANI_U8 set_value)
{
eHalStatus status;
int ret_val = 0;
if (0 != wlan_hdd_validate_context(hddctx)) {
ret_val = -EAGAIN;
goto exit;
}
if (!hddctx->cfg_ini->goptimize_chan_avoid_event) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("goptimize_chan_avoid_event ini param disabled"));
ret_val = -EAGAIN;
goto exit;
}
status = sme_enable_disable_chanavoidind_event(hddctx->hHal, set_value);
if (status != eHAL_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Failed to send chan avoid command to SME"));
ret_val = -EINVAL;
}
exit:
return ret_val;
}
#ifdef WLAN_FEATURE_PACKET_FILTERING
/**
* hdd_init_packet_filtering - allocate packet filter MC address list
* @hdd_ctx: pointer to hdd context
* @adapter: pointer to hdd_adapter_t
*
* This function allocates memory for link layer MC address list which will
* be communicated to firmware for packet filtering in HOST suspend state.
*
* Return: 0 on success, error number otherwise.
*/
int hdd_init_packet_filtering(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
adapter->mc_addr_list.addr =
vos_mem_malloc(hdd_ctx->max_mc_addr_list * ETH_ALEN);
if (NULL == adapter->mc_addr_list.addr) {
hddLog(LOGE, FL("Could not allocate Memory"));
return -ENOMEM;
}
vos_mem_zero(adapter->mc_addr_list.addr,
(hdd_ctx->max_mc_addr_list * ETH_ALEN));
return 0;
}
/**
* hdd_deinit_packet_filtering - deallocate packet filter MC address list
* @adapter: pointer to hdd_adapter_t
*
* Return: none
*/
void hdd_deinit_packet_filtering(hdd_adapter_t *adapter)
{
vos_mem_free(adapter->mc_addr_list.addr);
adapter->mc_addr_list.addr = NULL;
}
#endif
/**
* wlan_hdd_get_dfs_mode() - get ACS DFS mode
* @mode : cfg80211 DFS mode
*
* Return: return SAP ACS DFS mode else return ACS_DFS_MODE_NONE
*/
enum sap_acs_dfs_mode wlan_hdd_get_dfs_mode(enum dfs_mode mode)
{
switch (mode) {
case DFS_MODE_ENABLE:
return ACS_DFS_MODE_ENABLE;
break;
case DFS_MODE_DISABLE:
return ACS_DFS_MODE_DISABLE;
break;
case DFS_MODE_DEPRIORITIZE:
return ACS_DFS_MODE_DEPRIORITIZE;
break;
default:
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("ACS dfs mode is NONE"));
return ACS_DFS_MODE_NONE;
}
}
/**
* hdd_set_rps_cpu_mask - set RPS CPU mask for interfaces
* @hdd_ctx: pointer to hdd_context_t
*
* Return: none
*/
void hdd_set_rps_cpu_mask(hdd_context_t *hdd_ctx)
{
hdd_adapter_t *adapter;
hdd_adapter_list_node_t *adapter_node, *next;
VOS_STATUS status = VOS_STATUS_SUCCESS;
status = hdd_get_front_adapter (hdd_ctx, &adapter_node);
while (NULL != adapter_node && VOS_STATUS_SUCCESS == status) {
adapter = adapter_node->pAdapter;
if (NULL != adapter)
hdd_dp_util_send_rps_ind(adapter);
status = hdd_get_next_adapter (hdd_ctx, adapter_node, &next);
adapter_node = next;
}
}
/**
* hdd_initialize_adapter_common() - initialize completion variables
* @adapter: pointer to hdd_adapter_t
*
* Return: none
*/
void hdd_initialize_adapter_common(hdd_adapter_t *adapter)
{
if (NULL == adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: adapter is NULL ", __func__);
return;
}
init_completion(&adapter->session_open_comp_var);
init_completion(&adapter->session_close_comp_var);
init_completion(&adapter->disconnect_comp_var);
init_completion(&adapter->linkup_event_var);
init_completion(&adapter->cancel_rem_on_chan_var);
init_completion(&adapter->rem_on_chan_ready_event);
init_completion(&adapter->offchannel_tx_event);
init_completion(&adapter->tx_action_cnf_event);
#ifdef FEATURE_WLAN_TDLS
init_completion(&adapter->tdls_add_station_comp);
init_completion(&adapter->tdls_del_station_comp);
init_completion(&adapter->tdls_mgmt_comp);
init_completion(&adapter->tdls_link_establish_req_comp);
#endif
#ifdef WLAN_FEATURE_RMC
init_completion(&adapter->ibss_peer_info_comp);
#endif /* WLAN_FEATURE_RMC */
init_completion(&adapter->ula_complete);
init_completion(&adapter->change_country_code);
init_completion(&adapter->smps_force_mode_comp_var);
init_completion(&adapter->scan_info.scan_req_completion_event);
init_completion(&adapter->scan_info.abortscan_event_var);
return;
}
//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");
#if defined(QCA_WIFI_FTM)
module_param(con_mode, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#else
module_param_call(con_mode, con_mode_handler, param_get_int, &con_mode,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
module_param_call(fwpath, fwpath_changed_handler, param_get_string, &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);