blob: 631f435377da841cea3de4f7d9913e0ac9ae7f35 [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 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"));
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 <=