blob: 3b1b38f418c7c324f5d24bdf7d8563a2e4a53f3e [file] [log] [blame]
/*
* Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**========================================================================
\file wlan_hdd_p2p.c
\brief WLAN Host Device Driver implementation for P2P commands interface
========================================================================*/
#include <wlan_hdd_includes.h>
#include <wlan_hdd_hostapd.h>
#include <net/cfg80211.h>
#include "sme_Api.h"
#include "sme_QosApi.h"
#include "wlan_hdd_p2p.h"
#include "sapApi.h"
#include "wlan_hdd_main.h"
#include "vos_trace.h"
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <net/ieee80211_radiotap.h>
#ifdef FEATURE_WLAN_TDLS
#include "wlan_hdd_tdls.h"
#endif
#include "wlan_hdd_trace.h"
#include "vos_types.h"
#include "vos_trace.h"
#include "vos_sched.h"
//Ms to Micro Sec
#define MS_TO_MUS(x) ((x)*1000);
tANI_U8* hdd_getActionString( tANI_U16 MsgType )
{
switch (MsgType)
{
CASE_RETURN_STRING(SIR_MAC_ACTION_SPECTRUM_MGMT);
CASE_RETURN_STRING(SIR_MAC_ACTION_QOS_MGMT);
CASE_RETURN_STRING(SIR_MAC_ACTION_DLP);
CASE_RETURN_STRING(SIR_MAC_ACTION_BLKACK);
CASE_RETURN_STRING(SIR_MAC_ACTION_PUBLIC_USAGE);
CASE_RETURN_STRING(SIR_MAC_ACTION_RRM);
CASE_RETURN_STRING(SIR_MAC_ACTION_FAST_BSS_TRNST);
CASE_RETURN_STRING(SIR_MAC_ACTION_HT);
CASE_RETURN_STRING(SIR_MAC_ACTION_SA_QUERY);
CASE_RETURN_STRING(SIR_MAC_ACTION_PROT_DUAL_PUB);
CASE_RETURN_STRING(SIR_MAC_ACTION_WNM);
CASE_RETURN_STRING(SIR_MAC_ACTION_UNPROT_WNM);
CASE_RETURN_STRING(SIR_MAC_ACTION_TDLS);
CASE_RETURN_STRING(SIR_MAC_ACITON_MESH);
CASE_RETURN_STRING(SIR_MAC_ACTION_MHF);
CASE_RETURN_STRING(SIR_MAC_SELF_PROTECTED);
CASE_RETURN_STRING(SIR_MAC_ACTION_WME);
CASE_RETURN_STRING(SIR_MAC_ACTION_VHT);
default:
return ("UNKNOWN");
}
}
#ifdef WLAN_FEATURE_P2P_DEBUG
#define MAX_P2P_ACTION_FRAME_TYPE 9
const char *p2p_action_frame_type[]={"GO Negotiation Request",
"GO Negotiation Response",
"GO Negotiation Confirmation",
"P2P Invitation Request",
"P2P Invitation Response",
"Device Discoverability Request",
"Device Discoverability Response",
"Provision Discovery Request",
"Provision Discovery Response"};
/* We no need to protect this variable since
* there is no chance of race to condition
* and also not make any complicating the code
* just for debugging log
*/
tP2PConnectionStatus globalP2PConnectionStatus = P2P_NOT_ACTIVE;
#endif
#define MAX_TDLS_ACTION_FRAME_TYPE 11
const char *tdls_action_frame_type[] = {"TDLS Setup Request",
"TDLS Setup Response",
"TDLS Setup Confirm",
"TDLS Teardown",
"TDLS Peer Traffic Indication",
"TDLS Channel Switch Request",
"TDLS Channel Switch Response",
"TDLS Peer PSM Request",
"TDLS Peer PSM Response",
"TDLS Peer Traffic Response",
"TDLS Discovery Request" };
extern struct net_device_ops net_ops_struct;
static int hdd_wlan_add_rx_radiotap_hdr( struct sk_buff *skb,
int rtap_len, int flag );
static void hdd_wlan_tx_complete( hdd_adapter_t* pAdapter,
hdd_cfg80211_state_t* cfgState,
tANI_BOOLEAN actionSendSuccess );
static void hdd_sendMgmtFrameOverMonitorIface( hdd_adapter_t *pMonAdapter,
tANI_U32 nFrameLength,
tANI_U8* pbFrames,
tANI_U8 frameType );
static v_BOOL_t wlan_hdd_is_type_p2p_action( const u8 *buf, uint32_t len )
{
const u8 *ouiPtr;
if (len < WLAN_HDD_PUBLIC_ACTION_FRAME_SUB_TYPE_OFFSET + 1) {
return VOS_FALSE;
}
if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_CATEGORY_OFFSET] !=
WLAN_HDD_PUBLIC_ACTION_FRAME ) {
return VOS_FALSE;
}
if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_ACTION_OFFSET] !=
WLAN_HDD_VENDOR_SPECIFIC_ACTION ) {
return VOS_FALSE;
}
ouiPtr = &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_OFFSET];
if ( WPA_GET_BE24(ouiPtr) != WLAN_HDD_WFA_OUI ) {
return VOS_FALSE;
}
if ( buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_TYPE_OFFSET] !=
WLAN_HDD_WFA_P2P_OUI_TYPE ) {
return VOS_FALSE;
}
return VOS_TRUE;
}
static bool hdd_p2p_is_action_type_rsp( const u8 *buf, uint32_t len )
{
tActionFrmType actionFrmType;
if ( wlan_hdd_is_type_p2p_action(buf, len) )
{
actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_SUB_TYPE_OFFSET];
if ( actionFrmType != WLAN_HDD_INVITATION_REQ &&
actionFrmType != WLAN_HDD_GO_NEG_REQ &&
actionFrmType != WLAN_HDD_DEV_DIS_REQ &&
actionFrmType != WLAN_HDD_PROV_DIS_REQ )
return VOS_TRUE;
}
return VOS_FALSE;
}
eHalStatus wlan_hdd_remain_on_channel_callback( tHalHandle hHal, void* pCtx,
eHalStatus status )
{
hdd_adapter_t *pAdapter = (hdd_adapter_t*) pCtx;
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
rem_on_channel_request_type_t req_type;
if (pHddCtx == NULL)
{
hddLog(LOGE, "%s: Hdd Context is NULL", __func__);
return eHAL_STATUS_FAILURE;
}
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx == NULL)
{
hddLog( LOGW,
"%s: No Rem on channel pending for which Rsp is received", __func__);
mutex_unlock(&pHddCtx->roc_lock);
return eHAL_STATUS_SUCCESS;
}
hddLog( VOS_TRACE_LEVEL_INFO,
"Received ROC rsp (request type %d, channel %d, cookie %llu",
pRemainChanCtx->rem_on_chan_request,
pRemainChanCtx->chan.center_freq,
pRemainChanCtx->cookie);
if(!VOS_IS_STATUS_SUCCESS(vos_timer_stop(
&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer"));
}
if(!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL("Failed to destroy hdd_remain_on_chan_timer"));
}
if ( REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request )
{
if( cfgState->buf )
{
hddLog( LOG1,
"%s: We need to receive yet an ack from one of tx packet",
__func__);
}
cfg80211_remain_on_channel_expired(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
pRemainChanCtx->dev->ieee80211_ptr,
#else
pRemainChanCtx->dev,
#endif
pRemainChanCtx->cookie,
&pRemainChanCtx->chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
pRemainChanCtx->chan_type,
#endif
GFP_KERNEL);
pAdapter->lastRocTs = vos_timer_get_system_time();
}
req_type = pRemainChanCtx->rem_on_chan_request;
mutex_unlock(&pHddCtx->roc_lock);
if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
tANI_U8 sessionId = pAdapter->sessionId;
if( REMAIN_ON_CHANNEL_REQUEST == req_type )
{
sme_DeregisterMgmtFrame(
hHal, sessionId,
(SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0 );
}
}
else if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
WLANSAP_DeRegisterMgmtFrame(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0 );
}
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if ( pRemainChanCtx )
{
/* Trigger kernel panic if ROC timer state is not set to unused state
* before freeing the ROC ctx.
*/
if (VOS_TIMER_STATE_UNUSED != vos_timer_getCurrentState(
&pRemainChanCtx->hdd_remain_on_chan_timer))
VOS_BUG(0);
if (pRemainChanCtx->action_pkt_buff.frame_ptr != NULL
&& pRemainChanCtx->action_pkt_buff.frame_length != 0)
{
vos_mem_free(pRemainChanCtx->action_pkt_buff.frame_ptr);
}
hddLog( LOG1, FL(
"Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"),
cfgState->remain_on_chan_ctx);
vos_mem_free( pRemainChanCtx );
pRemainChanCtx = NULL;
cfgState->remain_on_chan_ctx = NULL;
}
mutex_unlock(&pHddCtx->roc_lock);
if (eHAL_STATUS_SUCCESS != status)
complete(&pAdapter->rem_on_chan_ready_event);
complete(&pAdapter->cancel_rem_on_chan_var);
pAdapter->is_roc_inprogress = FALSE;
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return eHAL_STATUS_SUCCESS;
}
VOS_STATUS wlan_hdd_cancel_existing_remain_on_channel(hdd_adapter_t *pAdapter)
{
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (NULL == pHddCtx)
{
hddLog(LOGE, "%s: HddCtx is NULL", __func__);
return VOS_STATUS_E_FAILURE;
}
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if(pRemainChanCtx != NULL)
{
if(VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState(
&pRemainChanCtx->hdd_remain_on_chan_timer))
{
hddLog(VOS_TRACE_LEVEL_INFO,
"Cancel Existing ROC (cookie=%llu)",
pRemainChanCtx->cookie);
if(!VOS_IS_STATUS_SUCCESS(vos_timer_stop(
&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer"));
}
}
/* Wait till remain on channel ready indication before issuing cancel
* remain on channel request, otherwise if remain on channel not
* received and if the driver issues cancel remain on channel then lim
* will be in unknown state.
*/
if (pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress != TRUE)
{
mutex_unlock(&pHddCtx->roc_lock);
status = wait_for_completion_interruptible_timeout(
&pAdapter->rem_on_chan_ready_event,
msecs_to_jiffies(WAIT_REM_CHAN_READY));
if (0 >= status)
{
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx)
pRemainChanCtx->is_pending_roc_cancelled = TRUE;
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGE,
"%s: timeout waiting for remain on channel"
" ready indication %d",
__func__, status);
vos_fatal_event_logs_req(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_HDD_TIME_OUT,
FALSE, TRUE);
return VOS_STATUS_E_FAILURE;
}
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (NULL == pRemainChanCtx)
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGE,
"%s-%d: pRemainChanCtx is NULL",
__func__, __LINE__);
return VOS_STATUS_E_FAILURE;
}
/* Check again if cancel remain on channel is started.
* If its started wait for its completiona and return.
*/
if (TRUE == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress)
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOG1,
"ROC timer cancellation in progress,"
" wait for completion");
status = wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
if (0 >= status)
{
hddLog( LOGE,
"%s:wait on cancel_rem_on_chan_var failed %d",
__func__, status);
return VOS_STATUS_E_FAILURE;
}
return VOS_STATUS_SUCCESS;
}
else
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE;
INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var);
mutex_unlock(&pHddCtx->roc_lock);
/* Issue abort remain on chan request to sme.
* The remain on channel callback will make sure the remain_on_chan
* expired event is sent.
*/
if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ))
{
if (eHAL_STATUS_SUCCESS !=
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ),
pAdapter->sessionId ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Failed to Cancel Remain on Channel"));
}
}
else if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
WLANSAP_CancelRemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
}
status = wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
if (0 >= status)
{
hddLog(LOGE,
FL("Timeout waiting for cancel remain on channel ready indication %d"),
status);
return VOS_STATUS_E_FAILURE;
}
}
else
{
hddLog( LOG1,
"ROC timer cancellation in progress,"
" wait for completion");
mutex_unlock(&pHddCtx->roc_lock);
status = wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
if (0 >= status)
{
hddLog( LOGE,
"%s:wait on cancel_rem_on_chan_var failed %d",
__func__, status);
return VOS_STATUS_E_FAILURE;
}
return VOS_STATUS_SUCCESS;
}
}
else
{
hddLog(LOG1,
"%s: remain_on_chan_ctx is NULL", __func__);
mutex_unlock(&pHddCtx->roc_lock);
}
return VOS_STATUS_SUCCESS;
}
int wlan_hdd_check_remain_on_channel(hdd_adapter_t *pAdapter)
{
int status = 0;
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
if(WLAN_HDD_P2P_GO != pAdapter->device_mode)
{
//Cancel Existing Remain On Channel
//If no action frame is pending
if( cfgState->remain_on_chan_ctx != NULL)
{
//Check whether Action Frame is pending or not
if( cfgState->buf == NULL)
{
wlan_hdd_cancel_existing_remain_on_channel(pAdapter);
}
else
{
hddLog(VOS_TRACE_LEVEL_DEBUG,
"Cannot Cancel Existing Remain on Channel");
status = -EBUSY;
}
}
}
return status;
}
void wlan_hdd_remain_on_chan_timeout(void *data)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *)data;
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_cfg80211_state_t *cfgState;
hdd_context_t *pHddCtx;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic))
{
hddLog( LOGE, FL("pAdapter is invalid %pK !!!"), pAdapter);
return;
}
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (NULL == pRemainChanCtx)
{
hddLog( LOGE, FL("No Remain on channel is pending"));
mutex_unlock(&pHddCtx->roc_lock);
return;
}
if ( TRUE == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress )
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGE, FL("Cancellation already in progress"));
return;
}
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE;
INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var);
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOG1,"%s: Cancel Remain on Channel on timeout", __func__);
if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
if (eHAL_STATUS_SUCCESS !=
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter),
pAdapter->sessionId ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Failed to Cancel Remain on Channel"));
}
}
else if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
WLANSAP_CancelRemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
}
wlan_hdd_start_stop_tdls_source_timer(pHddCtx, eTDLS_SUPPORT_ENABLED);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
}
static int wlan_hdd_p2p_start_remain_on_channel(
hdd_adapter_t *pAdapter)
{
VOS_STATUS status = VOS_STATUS_SUCCESS;
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter_temp;
v_BOOL_t isGoPresent = VOS_FALSE;
hdd_context_t *pHddCtx;
hdd_cfg80211_state_t *cfgState;
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
rem_on_channel_request_type_t request_type;
unsigned int duration;
v_U16_t hw_value;
int ret = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Adapter is NULL",__func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
if (NULL == cfgState)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: cfgState is not valid ",__func__);
return -EINVAL;
}
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if ( pRemainChanCtx == NULL)
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGE,
"%s-%d: pRemainChanCtx is NULL",
__func__, __LINE__);
return ret;
}
request_type = pRemainChanCtx->rem_on_chan_request;
/* Initialize Remain on chan timer */
status = vos_timer_init(&pRemainChanCtx->hdd_remain_on_chan_timer,
VOS_TIMER_TYPE_SW,
wlan_hdd_remain_on_chan_timeout,
pAdapter);
if (status != VOS_STATUS_SUCCESS)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
FL("Not able to initalize remain_on_chan timer"));
hddLog( LOG1, FL(
"Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"),
cfgState->remain_on_chan_ctx);
cfgState->remain_on_chan_ctx = NULL;
vos_mem_free(pRemainChanCtx);
mutex_unlock(&pHddCtx->roc_lock);
return -EINVAL;
}
duration = pRemainChanCtx->duration;
hw_value = pRemainChanCtx->chan.hw_value;
mutex_unlock(&pHddCtx->roc_lock);
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter_temp = pAdapterNode->pAdapter;
if (WLAN_HDD_P2P_GO == pAdapter_temp->device_mode)
{
isGoPresent = VOS_TRUE;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
//call sme API to start remain on channel.
if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
tANI_U8 sessionId = pAdapter->sessionId;
//call sme API to start remain on channel.
if (eHAL_STATUS_SUCCESS != sme_RemainOnChannel(
WLAN_HDD_GET_HAL_CTX(pAdapter), sessionId,
hw_value, duration,
wlan_hdd_remain_on_channel_callback, pAdapter,
(tANI_U8)(request_type == REMAIN_ON_CHANNEL_REQUEST)? TRUE:FALSE))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL(" RemainOnChannel returned fail"));
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
hddLog( LOG1, FL(
"Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"),
cfgState->remain_on_chan_ctx);
if (pRemainChanCtx)
{
if(!VOS_IS_STATUS_SUCCESS(vos_timer_destroy
(&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL(
"Failed to destroy hdd_remain_on_chan_timer"));
}
vos_mem_free(pRemainChanCtx);
cfgState->remain_on_chan_ctx = NULL;
}
mutex_unlock(&pHddCtx->roc_lock);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
if( REMAIN_ON_CHANNEL_REQUEST == request_type)
{
if( eHAL_STATUS_SUCCESS != sme_RegisterMgmtFrame(
WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId, (SIR_MAC_MGMT_FRAME << 2) |
(SIR_MAC_MGMT_PROBE_REQ << 4), NULL, 0 ))
{
hddLog(VOS_TRACE_LEVEL_ERROR, "sme_RegisterMgmtFrame returned fail");
}
}
}
else if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
//call sme API to start remain on channel.
if (VOS_STATUS_SUCCESS != WLANSAP_RemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
hw_value, duration,
wlan_hdd_remain_on_channel_callback, pAdapter ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_RemainOnChannel returned fail", __func__);
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
hddLog( LOG1, FL(
"Freeing ROC ctx cfgState->remain_on_chan_ctx=%pK"),
cfgState->remain_on_chan_ctx);
if (pRemainChanCtx)
{
if(!VOS_IS_STATUS_SUCCESS(vos_timer_destroy
(&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL(
"Failed to destroy hdd_remain_on_chan_timer"));
}
vos_mem_free (pRemainChanCtx);
cfgState->remain_on_chan_ctx = NULL;
}
mutex_unlock(&pHddCtx->roc_lock);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
if (VOS_STATUS_SUCCESS != WLANSAP_RegisterMgmtFrame(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
(SIR_MAC_MGMT_FRAME << 2) | ( SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0 ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_RegisterMgmtFrame returned fail", __func__);
WLANSAP_CancelRemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
}
wlan_hdd_start_stop_tdls_source_timer(pHddCtx, eTDLS_SUPPORT_DISABLED);
pAdapter->is_roc_inprogress = TRUE;
EXIT();
return 0;
}
static int wlan_hdd_request_remain_on_channel( struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 *cookie,
rem_on_channel_request_type_t request_type )
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_remain_on_chan_ctx_t *pRemainChanCtx;
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
hdd_context_t *pHddCtx = NULL;
VOS_STATUS checkReadyInd;
hdd_adapter_t *pStaAdapter;
int status = 0;
ENTER();
if (NULL == pAdapter)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: HDD adapter is Null", __func__);
return -ENODEV;
}
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %d",
__func__,pAdapter->device_mode);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
hddLog(VOS_TRACE_LEVEL_INFO,
"chan(hw_val)0x%x chan(centerfreq) %d chan type 0x%x, dur %d,"
" request type %d, cookie %llu",
chan->hw_value, chan->center_freq, channel_type, duration,
request_type, *cookie);
#else
hddLog(VOS_TRACE_LEVEL_INFO,
"chan(hw_val)0x%x chan(centerfreq) %d, duration %d"
" reuest type %d, cookie %llu", chan->hw_value, chan->center_freq,
duration, request_type, *cookie );
#endif
//Cancel existing remain On Channel if any
checkReadyInd = wlan_hdd_cancel_existing_remain_on_channel(pAdapter);
if (VOS_STATUS_SUCCESS != checkReadyInd)
{
hddLog( LOGE, FL("Cancel Roc in progress"));
return -EBUSY;
}
if (TRUE == pHddCtx->btCoexModeSet)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("BTCoex Mode operation in progress"));
return -EBUSY;
}
if(hdd_isConnectionInProgress((hdd_context_t *)pAdapter->pHddCtx, NULL,
NULL))
{
hddLog( LOGE,
"%s: Connection is in progress", __func__);
return -EBUSY;
}
mutex_lock(&pHddCtx->roc_lock);
if (cfgState->remain_on_chan_ctx)
VOS_BUG(0);
pRemainChanCtx = vos_mem_malloc( sizeof(hdd_remain_on_chan_ctx_t) );
if( NULL == pRemainChanCtx )
{
hddLog(VOS_TRACE_LEVEL_FATAL,
"%s: Not able to allocate memory for Channel context",
__func__);
mutex_unlock(&pHddCtx->roc_lock);
return -ENOMEM;
}
vos_mem_zero(pRemainChanCtx, sizeof (hdd_remain_on_chan_ctx_t));
vos_mem_copy( &pRemainChanCtx->chan, chan,
sizeof(struct ieee80211_channel) );
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
pRemainChanCtx->chan_type = channel_type;
#endif
pRemainChanCtx->duration = duration;
pRemainChanCtx->dev = dev;
*cookie = (uintptr_t) pRemainChanCtx;
pRemainChanCtx->cookie = *cookie;
pRemainChanCtx->rem_on_chan_request = request_type;
cfgState->remain_on_chan_ctx = pRemainChanCtx;
cfgState->current_freq = chan->center_freq;
pRemainChanCtx->action_pkt_buff.freq = 0;
pRemainChanCtx->action_pkt_buff.frame_ptr = NULL;
pRemainChanCtx->action_pkt_buff.frame_length = 0;
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = FALSE;
pRemainChanCtx->is_pending_roc_cancelled = FALSE;
INIT_COMPLETION(pAdapter->rem_on_chan_ready_event);
if (REMAIN_ON_CHANNEL_REQUEST == request_type)
{
pStaAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
if((NULL != pStaAdapter)&&
hdd_connIsConnected( WLAN_HDD_GET_STATION_CTX_PTR(pStaAdapter)))
{
if (pAdapter->lastRocTs !=0 &&
((vos_timer_get_system_time() - pAdapter->lastRocTs )
< pHddCtx->cfg_ini->gP2PListenDeferInterval))
{
if (pRemainChanCtx->duration > HDD_P2P_MAX_ROC_DURATION)
pRemainChanCtx->duration = HDD_P2P_MAX_ROC_DURATION;
mutex_unlock(&pHddCtx->roc_lock);
schedule_delayed_work(&pAdapter->roc_work,
msecs_to_jiffies(pHddCtx->cfg_ini->gP2PListenDeferInterval));
hddLog(VOS_TRACE_LEVEL_INFO, "Defer interval is %hu, pAdapter %pK",
pHddCtx->cfg_ini->gP2PListenDeferInterval, pAdapter);
return 0;
}
}
}
mutex_unlock(&pHddCtx->roc_lock);
status = wlan_hdd_p2p_start_remain_on_channel(pAdapter);
EXIT();
return status;
}
int __wlan_hdd_cfg80211_remain_on_channel( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
struct ieee80211_channel *chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 *cookie )
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct net_device *dev = wdev->netdev;
#endif
hdd_adapter_t *pAdapter;
hdd_context_t *pHddCtx;
int ret = 0;
ENTER();
pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
if (NULL == pAdapter)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: Adapter is NULL",__func__);
return -EINVAL;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
return ret;
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_REMAIN_ON_CHANNEL,
pAdapter->sessionId, REMAIN_ON_CHANNEL_REQUEST));
ret = wlan_hdd_request_remain_on_channel(wiphy, dev,
chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
channel_type,
#endif
duration, cookie,
REMAIN_ON_CHANNEL_REQUEST);
EXIT();
return ret;
}
int wlan_hdd_cfg80211_remain_on_channel( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
struct ieee80211_channel *chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 *cookie )
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_remain_on_channel(wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
wdev,
#else
dev,
#endif
chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
channel_type,
#endif
duration, cookie);
vos_ssr_unprotect(__func__);
return ret;
}
void hdd_remainChanReadyHandler( hdd_adapter_t *pAdapter )
{
hdd_cfg80211_state_t *cfgState = NULL;
hdd_remain_on_chan_ctx_t* pRemainChanCtx = NULL;
hdd_context_t *pHddCtx;
VOS_STATUS status;
if (NULL == pAdapter)
{
hddLog(LOGE, FL("pAdapter is NULL"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
hddLog(LOGE, FL("pHddCtx is NULL"));
return;
}
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
pAdapter->startRocTs = vos_timer_get_system_time();
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if( pRemainChanCtx != NULL )
{
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_REMAINCHANREADYHANDLER,
pAdapter->sessionId, pRemainChanCtx->duration));
//start timer for actual duration
status = vos_timer_start(&pRemainChanCtx->hdd_remain_on_chan_timer,
(pRemainChanCtx->duration));
if (VOS_STATUS_SUCCESS!=status)
{
hddLog( LOGE, FL("Remain on Channel timer start failed"));
}
if( REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request)
{
cfg80211_ready_on_channel(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
pAdapter->dev->ieee80211_ptr,
#else
pAdapter->dev,
#endif
(uintptr_t)pRemainChanCtx,
&pRemainChanCtx->chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
pRemainChanCtx->chan_type,
#endif
pRemainChanCtx->duration, GFP_KERNEL );
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
else if( OFF_CHANNEL_ACTION_TX == pRemainChanCtx->rem_on_chan_request)
{
complete(&pAdapter->offchannel_tx_event);
}
#endif
// Check for cached action frame
if ( pRemainChanCtx->action_pkt_buff.frame_length != 0 )
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr,
pRemainChanCtx->action_pkt_buff.freq, 0,
pRemainChanCtx->action_pkt_buff.frame_ptr,
pRemainChanCtx->action_pkt_buff.frame_length,
NL80211_RXMGMT_FLAG_ANSWERED);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0))
cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr,
pRemainChanCtx->action_pkt_buff.freq, 0,
pRemainChanCtx->action_pkt_buff.frame_ptr,
pRemainChanCtx->action_pkt_buff.frame_length,
NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
cfg80211_rx_mgmt( pAdapter->dev->ieee80211_ptr,
pRemainChanCtx->action_pkt_buff.freq, 0,
pRemainChanCtx->action_pkt_buff.frame_ptr,
pRemainChanCtx->action_pkt_buff.frame_length,
GFP_ATOMIC );
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
cfg80211_rx_mgmt( pAdapter->dev,
pRemainChanCtx->action_pkt_buff.freq, 0,
pRemainChanCtx->action_pkt_buff.frame_ptr,
pRemainChanCtx->action_pkt_buff.frame_length,
GFP_ATOMIC );
#else
cfg80211_rx_mgmt( pAdapter->dev,
pRemainChanCtx->action_pkt_buff.freq,
pRemainChanCtx->action_pkt_buff.frame_ptr,
pRemainChanCtx->action_pkt_buff.frame_length,
GFP_ATOMIC );
#endif //LINUX_VERSION_CODE
hddLog( LOGE, "%s: Sent cached action frame to supplicant", __func__);
vos_mem_free(pRemainChanCtx->action_pkt_buff.frame_ptr);
pRemainChanCtx->action_pkt_buff.frame_length = 0;
pRemainChanCtx->action_pkt_buff.freq = 0;
pRemainChanCtx->action_pkt_buff.frame_ptr = NULL;
}
hddLog( VOS_TRACE_LEVEL_INFO, "Ready on chan ind (cookie=%llu)",
pRemainChanCtx->cookie);
complete(&pAdapter->rem_on_chan_ready_event);
if (TRUE == pRemainChanCtx->is_pending_roc_cancelled)
{
mutex_unlock(&pHddCtx->roc_lock);
/* since pRemainChanCtx->is_pending_roc_cancelled is
* set, it means Cancel Reamain on channel command is
* pending because remain on channel event was not
* ready when cancel ROC was issued.So issue
* cancel ROC now.
*/
wlan_hdd_cancel_existing_remain_on_channel(pAdapter);
}
else
{
mutex_unlock(&pHddCtx->roc_lock);
}
}
else
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGW, "%s: No Pending Remain on channel Request", __func__);
}
return;
}
int __wlan_hdd_cfg80211_cancel_remain_on_channel( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
u64 cookie )
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct net_device *dev = wdev->netdev;
#endif
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
int status;
u64 cookie_dummy;
ENTER();
cookie_dummy = cookie << 32;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_CFG80211_CANCEL_REMAIN_ON_CHANNEL,
pAdapter->sessionId, cookie_dummy));
hddLog( LOG1, "Cancel remain on channel req");
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog( LOG1, "Cancel remain on channel req (cookie = %llu)", cookie);
/* FIXME cancel currently running remain on chan.
* Need to check cookie and cancel accordingly
*/
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if( (cfgState->remain_on_chan_ctx == NULL) ||
(cfgState->remain_on_chan_ctx->cookie != cookie) )
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOGE,
"%s: No Remain on channel pending with specified cookie value",
__func__);
return -EINVAL;
}
if (TRUE != pRemainChanCtx->is_pending_roc_cancelled)
{
mutex_unlock(&pHddCtx->roc_lock);
/* wait until remain on channel ready event received
* for already issued remain on channel request */
status = wait_for_completion_interruptible_timeout(&pAdapter->rem_on_chan_ready_event,
msecs_to_jiffies(WAIT_REM_CHAN_READY));
if (0 >= status)
{
hddLog( LOGE,
"%s: timeout waiting for remain on channel ready indication %d",
__func__, status);
mutex_lock(&pHddCtx->roc_lock);
if (cfgState->remain_on_chan_ctx)
cfgState->remain_on_chan_ctx->is_pending_roc_cancelled = TRUE;
mutex_unlock(&pHddCtx->roc_lock);
vos_fatal_event_logs_req(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_HDD_TIME_OUT,
FALSE, TRUE);
return 0;
}
mutex_lock(&pHddCtx->roc_lock);
}
else
{
hddLog( LOG1, FL("Cancel ROC event is already pending, "
"waiting for ready on channel indication.") );
mutex_unlock(&pHddCtx->roc_lock);
return 0;
}
if (NULL != cfgState->remain_on_chan_ctx)
{
if(!VOS_IS_STATUS_SUCCESS(vos_timer_stop(
&cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer"));
}
if (TRUE == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress)
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog( LOG1,
FL("ROC timer cancellation in progress,"
" wait for completion"));
status = wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
if (0 >= status)
{
hddLog( LOGE,
"%s:wait on cancel_rem_on_chan_var failed %d",
__func__, status);
}
return 0;
}
else
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = TRUE;
}
INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var);
mutex_unlock(&pHddCtx->roc_lock);
/* Issue abort remain on chan request to sme.
* The remain on channel callback will make sure the remain_on_chan
* expired event is sent.
*/
if ( ( WLAN_HDD_INFRA_STATION == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_CLIENT == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode ))
{
tANI_U8 sessionId = pAdapter->sessionId;
if (eHAL_STATUS_SUCCESS !=
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ),
sessionId ))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Failed to Cancel Remain on Channel"));
}
}
else if (WLAN_HDD_P2P_GO == pAdapter->device_mode)
{
WLANSAP_CancelRemainOnChannel(
(WLAN_HDD_GET_CTX(pAdapter))->pvosContext);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Invalid device_mode = %d",
__func__, pAdapter->device_mode);
return -EIO;
}
status = wait_for_completion_interruptible_timeout(&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
if (0 >= status)
{
hddLog( LOGE,
"%s:wait on cancel_rem_on_chan_var failed %d", __func__, status);
}
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
EXIT();
return 0;
}
int wlan_hdd_cfg80211_cancel_remain_on_channel( struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
u64 cookie )
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
wdev,
#else
dev,
#endif
cookie);
vos_ssr_unprotect(__func__);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int __wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
enum nl80211_channel_type channel_type,
bool channel_type_valid,
#endif
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie )
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
int __wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie )
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
int __wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, u64 *cookie )
#else
int __wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
bool channel_type_valid,
const u8 *buf, size_t len, u64 *cookie )
#endif //LINUX_VERSION_CODE
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct net_device *dev = wdev->netdev;
#endif
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_adapter_t *pGoAdapter;
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
tANI_U8 type;
tANI_U8 subType;
tActionFrmType actionFrmType = WLAN_HDD_ACTION_FRM_TYPE_MAX;
bool noack = 0;
int status;
uint32_t mgmt_hdr_len = sizeof(struct ieee80211_hdr_3addr);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
uint8_t home_ch = 0;
#endif
eHalStatus hal_status;
ENTER();
if (len < mgmt_hdr_len + 1) {
hddLog(VOS_TRACE_LEVEL_ERROR,"Invalid Length");
return -EINVAL;
}
type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]);
subType = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]);
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_ACTION, pAdapter->sessionId,
pAdapter->device_mode ));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %d type: %d",
__func__, pAdapter->device_mode, type);
/* When frame to be transmitted is auth mgmt, then trigger
* sme_send_mgmt_tx to send auth frame.
*/
if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
(type == SIR_MAC_MGMT_FRAME && subType == SIR_MAC_MGMT_AUTH)) {
hal_status = sme_send_mgmt_tx(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, buf, len);
if (HAL_STATUS_SUCCESS(hal_status))
return 0;
else
return -EINVAL;
}
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType == SIR_MAC_MGMT_ACTION) &&
wlan_hdd_is_type_p2p_action(
&buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET], len - mgmt_hdr_len))
{
actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
#ifdef WLAN_FEATURE_P2P_DEBUG
if(actionFrmType >= MAX_P2P_ACTION_FRAME_TYPE)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] unknown[%d] ---> OTA",
actionFrmType);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] %s ---> OTA",
p2p_action_frame_type[actionFrmType]);
if( (actionFrmType == WLAN_HDD_PROV_DIS_REQ) &&
(globalP2PConnectionStatus == P2P_NOT_ACTIVE) )
{
globalP2PConnectionStatus = P2P_GO_NEG_PROCESS;
hddLog(LOGE,"[P2P State]Inactive state to "
"GO negotiation progress state");
}
else if( (actionFrmType == WLAN_HDD_GO_NEG_CNF) &&
(globalP2PConnectionStatus == P2P_GO_NEG_PROCESS) )
{
globalP2PConnectionStatus = P2P_GO_NEG_COMPLETED;
hddLog(LOGE,"[P2P State]GO nego progress to GO nego"
" completed state");
}
}
#endif
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
noack = dont_wait_for_ack;
#endif
//If the wait is coming as 0 with off channel set
//then set the wait to 200 ms
if (offchan && !wait)
{
wait = ACTION_FRAME_DEFAULT_WAIT;
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx)
{
tANI_U32 current_time = vos_timer_get_system_time();
int remaining_roc_time = ((int) pRemainChanCtx->duration -
(current_time - pAdapter->startRocTs));
if ( remaining_roc_time > ACTION_FRAME_DEFAULT_WAIT)
wait = remaining_roc_time;
}
mutex_unlock(&pHddCtx->roc_lock);
}
//Call sme API to send out a action frame.
// OR can we send it directly through data path??
// After tx completion send tx status back.
if ( ( WLAN_HDD_SOFTAP == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_GO == pAdapter->device_mode )
)
{
if (type == SIR_MAC_MGMT_FRAME)
{
if (subType == SIR_MAC_MGMT_PROBE_RSP)
{
/* Drop Probe response recieved from supplicant, as for GO and
SAP PE itself sends probe response
*/
goto err_rem_channel;
}
else if ((subType == SIR_MAC_MGMT_DISASSOC) ||
(subType == SIR_MAC_MGMT_DEAUTH))
{
/* During EAP failure or P2P Group Remove supplicant
* is sending del_station command to driver. From
* del_station function, Driver will send deauth frame to
* p2p client. No need to send disassoc frame from here.
* so Drop the frame here and send tx indication back to
* supplicant.
*/
tANI_U8 dstMac[ETH_ALEN] = {0};
memcpy(&dstMac, &buf[WLAN_HDD_80211_FRM_DA_OFFSET], ETH_ALEN);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Deauth/Disassoc received for STA:"
MAC_ADDRESS_STR,
__func__,
MAC_ADDR_ARRAY(dstMac));
goto err_rem_channel;
}
}
}
if( NULL != cfgState->buf )
{
if ( !noack )
{
hddLog( LOGE, "(%s):Previous P2P Action frame packet pending",
__func__);
hdd_cleanup_actionframe(pAdapter->pHddCtx, pAdapter);
}
else
{
hddLog( LOGE, "(%s):Pending Action frame packet return EBUSY",
__func__);
return -EBUSY;
}
}
if( subType == SIR_MAC_MGMT_ACTION)
{
hddLog( LOG1, "Action frame tx request : %s",
hdd_getActionString(buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET]));
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
if ( (( WLAN_HDD_SOFTAP == pAdapter->device_mode ) ||
( WLAN_HDD_P2P_GO == pAdapter->device_mode )) &&
(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags)))
{
home_ch = pAdapter->sessionCtx.ap.operatingChannel;
}
else if ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) &&
(pAdapter->sessionCtx.station.conn_info.connState ==
eConnectionState_Associated))
{
home_ch = pAdapter->sessionCtx.station.conn_info.operationChannel;
}
else
{
pGoAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_P2P_GO);
if (pGoAdapter && test_bit(SOFTAP_BSS_STARTED,
&pGoAdapter->event_flags))
home_ch = pGoAdapter->sessionCtx.ap.operatingChannel;
}
//If GO adapter exists and operating on same frequency
//then we will not request remain on channel
if (ieee80211_frequency_to_channel(chan->center_freq) == home_ch)
{
/* if GO exist and is not off channel
* wait time should be zero.
*/
wait = 0;
goto send_frame;
}
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
if( offchan && wait)
{
int status;
rem_on_channel_request_type_t req_type = OFF_CHANNEL_ACTION_TX;
// In case of P2P Client mode if we are already
// on the same channel then send the frame directly
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if( (pRemainChanCtx != NULL) &&
(cfgState->current_freq == chan->center_freq)
)
{
if ( VOS_TIMER_STATE_RUNNING == vos_timer_getCurrentState(
&pRemainChanCtx->hdd_remain_on_chan_timer) )
{
/* Some times FW is taking almost 500 msec for
* full 15 retries, which leads to ROC expiration
* by the time peer gets response from other peer.
* Therefore as part of temporary fix , in host
* ROC time is extended. For frames where we are
* expecting response from peer , its extended by
* 500 msec to make ROC wait time as 1 sec and
* in other cases its extended by 300 msec to make
* total ROC wait as 500 msec.
* TODO: FW needs to fix as why 15 retry is taking
* such long time.
*/
if ( actionFrmType == WLAN_HDD_INVITATION_REQ ||
actionFrmType == WLAN_HDD_GO_NEG_REQ ||
actionFrmType == WLAN_HDD_GO_NEG_RESP )
wait = wait + ACTION_FRAME_RSP_WAIT;
else if ( actionFrmType == WLAN_HDD_GO_NEG_CNF ||
actionFrmType == WLAN_HDD_INVITATION_RESP )
wait = wait + ACTION_FRAME_ACK_WAIT;
if (!VOS_IS_STATUS_SUCCESS(vos_timer_stop(
&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL("Failed to stop hdd_remain_on_chan_timer"));
}
status = vos_timer_start(
&pRemainChanCtx->hdd_remain_on_chan_timer,
wait);
mutex_unlock(&pHddCtx->roc_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
"action frame: extending the wait time %u",
wait);
if ( status != VOS_STATUS_SUCCESS )
{
hddLog( LOGE, "Remain on Channel timer start failed");
}
goto send_frame;
}
else
{
if ( TRUE ==
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress )
{
mutex_unlock(&pHddCtx->roc_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
"action frame tx: waiting for completion of ROC ");
status = wait_for_completion_interruptible_timeout(
&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies(WAIT_CANCEL_REM_CHAN));
if (0 >= status)
{
hddLog( LOGE,
"%s:wait on cancel_rem_on_chan_var failed %d",
__func__, status);
}
goto bypass_lock;
}
}
}
mutex_unlock(&pHddCtx->roc_lock);
bypass_lock:
hddLog(VOS_TRACE_LEVEL_INFO,
"action frame: Request ROC for wait time %u", wait);
INIT_COMPLETION(pAdapter->offchannel_tx_event);
status = wlan_hdd_request_remain_on_channel(wiphy, dev,
chan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
channel_type,
#endif
wait, cookie,
req_type);
if(0 != status)
{
if( (-EBUSY == status) &&
(cfgState->current_freq == chan->center_freq) )
{
goto send_frame;
}
goto err_rem_channel;
}
/* Wait for driver to be ready on the requested channel */
status = wait_for_completion_interruptible_timeout(
&pAdapter->offchannel_tx_event,
msecs_to_jiffies(WAIT_CHANGE_CHANNEL_FOR_OFFCHANNEL_TX));
if(0 >= status)
{
hddLog( LOGE, "wait on offchannel_tx_event failed %d", status);
goto err_rem_channel;
}
}
else if ( offchan )
{
/* Check before sending action frame
whether we already remain on channel */
if(NULL == cfgState->remain_on_chan_ctx)
{
goto err_rem_channel;
}
}
send_frame:
#endif
if(!noack)
{
cfgState->buf = vos_mem_malloc( len ); //buf;
if( cfgState->buf == NULL )
return -ENOMEM;
cfgState->len = len;
vos_mem_copy( cfgState->buf, buf, len);
mutex_lock(&pHddCtx->roc_lock);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
if( cfgState->remain_on_chan_ctx )
{
cfgState->action_cookie = cfgState->remain_on_chan_ctx->cookie;
*cookie = cfgState->action_cookie;
}
else
{
#endif
*cookie = (uintptr_t) cfgState->buf;
cfgState->action_cookie = *cookie;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
}
#endif
mutex_unlock(&pHddCtx->roc_lock);
}
if ( (WLAN_HDD_INFRA_STATION == pAdapter->device_mode) ||
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
tANI_U8 sessionId = pAdapter->sessionId;
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType == SIR_MAC_MGMT_ACTION) &&
wlan_hdd_is_type_p2p_action(
&buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET],
len - mgmt_hdr_len))
{
actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
hddLog(LOG1, "Tx Action Frame %u.", actionFrmType);
if (actionFrmType == WLAN_HDD_PROV_DIS_REQ)
{
cfgState->actionFrmState = HDD_PD_REQ_ACK_PENDING;
hddLog(LOG1, "%s: HDD_PD_REQ_ACK_PENDING.", __func__);
}
else if (actionFrmType == WLAN_HDD_GO_NEG_REQ)
{
cfgState->actionFrmState = HDD_GO_NEG_REQ_ACK_PENDING;
hddLog(LOG1, "%s: HDD_GO_NEG_REQ_ACK_PENDING.", __func__);
}
}
if (eHAL_STATUS_SUCCESS !=
sme_sendAction( WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId, buf, len, wait, noack))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: sme_sendAction returned fail", __func__);
goto err;
}
}
else if( ( WLAN_HDD_SOFTAP== pAdapter->device_mode ) ||
( WLAN_HDD_P2P_GO == pAdapter->device_mode )
)
{
if( VOS_STATUS_SUCCESS !=
WLANSAP_SendAction( (WLAN_HDD_GET_CTX(pAdapter))->pvosContext,
buf, len, 0 ) )
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: WLANSAP_SendAction returned fail", __func__);
goto err;
}
}
return 0;
err:
if(!noack)
{
hdd_sendActionCnf( pAdapter, FALSE );
}
return 0;
err_rem_channel:
*cookie = (uintptr_t)cfgState;
cfg80211_mgmt_tx_status(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
pAdapter->dev->ieee80211_ptr,
#else
pAdapter->dev,
#endif
*cookie, buf, len, FALSE, GFP_KERNEL );
EXIT();
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params, u64 *cookie)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
enum nl80211_channel_type channel_type,
bool channel_type_valid,
#endif
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie )
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
int wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie )
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
int wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan, bool offchan,
enum nl80211_channel_type channel_type,
bool channel_type_valid, unsigned int wait,
const u8 *buf, size_t len, u64 *cookie )
#else
int wlan_hdd_mgmt_tx( struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
bool channel_type_valid,
const u8 *buf, size_t len, u64 *cookie )
#endif //LINUX_VERSION_CODE
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0))
ret = __wlan_hdd_mgmt_tx(wiphy, wdev, params->chan, params->offchan,
params->wait, params->buf, params->len,
params->no_cck, params->dont_wait_for_ack,
cookie);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
ret = __wlan_hdd_mgmt_tx(wiphy, wdev,
chan, offchan,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0))
channel_type,
channel_type_valid,
#endif
wait,
buf, len, no_cck,
dont_wait_for_ack, cookie);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
ret = __wlan_hdd_mgmt_tx(wiphy, dev, chan, offchan,
channel_type, channel_type_valid, wait,
buf, len, no_cck,
dont_wait_for_ack, cookie);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
ret = __wlan_hdd_mgmt_tx(wiphy, dev, chan, offchan,
channel_type, channel_type_valid, wait,
buf, len, cookie);
#else
ret = __wlan_hdd_mgmt_tx(wiphy, dev, chan, channel_type,
channel_type_valid, buf, len, cookie);
#endif //LINUX_VERSION_CODE
vos_ssr_unprotect(__func__);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
u64 cookie_dummy;
cookie_dummy = cookie << 32;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_MGMT_TX_CANCEL_WAIT, NO_SESSION, cookie_dummy));
return wlan_hdd_cfg80211_cancel_remain_on_channel( wiphy, wdev, cookie );
}
#else
int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
u64 cookie_dummy;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
cookie_dummy = cookie << 32;
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_MGMT_TX_CANCEL_WAIT,
pAdapter->sessionId, cookie_dummy));
return wlan_hdd_cfg80211_cancel_remain_on_channel( wiphy, dev, cookie );
}
#endif
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, wdev, cookie);
vos_ssr_unprotect(__func__);
return ret;
}
#else
int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
int ret;
vos_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, dev, cookie);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
#endif
void hdd_sendActionCnf( hdd_adapter_t *pAdapter, tANI_BOOLEAN actionSendSuccess )
{
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
cfgState->actionFrmState = HDD_IDLE;
if( NULL == cfgState->buf )
{
return;
}
if (cfgState->is_go_neg_ack_received)
{
cfgState->is_go_neg_ack_received = 0;
/* Sometimes its possible that host may receive the ack for GO
* negotiation req after sending go negotaition confirmation,
* in such case drop the ack received for the go negotiation
* request, so that supplicant waits for the confirmation ack
* from firmware.
*/
hddLog( LOG1, FL("Drop the pending ack received in cfgState->actionFrmState %d"),
cfgState->actionFrmState);
return;
}
hddLog( LOG1, "Send Action cnf, actionSendSuccess %d", actionSendSuccess);
/* If skb is NULL it means this packet was received on CFG80211 interface
* else it was received on Monitor interface */
if( cfgState->skb == NULL )
{
/*
* buf is the same pointer it passed us to send. Since we are sending
* it through control path, we use different buffers.
* In case of mac80211, they just push it to the skb and pass the same
* data while sending tx ack status.
* */
cfg80211_mgmt_tx_status(
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
pAdapter->dev->ieee80211_ptr,
#else
pAdapter->dev,
#endif
cfgState->action_cookie,
cfgState->buf, cfgState->len, actionSendSuccess, GFP_KERNEL );
vos_mem_free( cfgState->buf );
cfgState->buf = NULL;
}
else
{
hdd_adapter_t* pMonAdapter =
hdd_get_adapter( pAdapter->pHddCtx, WLAN_HDD_MONITOR );
if( pMonAdapter == NULL )
{
hddLog( LOGE, "Not able to get Monitor Adapter");
cfgState->skb = NULL;
vos_mem_free( cfgState->buf );
cfgState->buf = NULL;
complete(&pAdapter->tx_action_cnf_event);
return;
}
/* Send TX completion feedback over monitor interface. */
hdd_wlan_tx_complete( pMonAdapter, cfgState, actionSendSuccess );
cfgState->skb = NULL;
vos_mem_free( cfgState->buf );
cfgState->buf = NULL;
/* Look for the next Mgmt packet to TX */
hdd_mon_tx_mgmt_pkt(pAdapter);
}
complete(&pAdapter->tx_action_cnf_event);
}
/**
* hdd_setP2pNoa
*
*FUNCTION:
* This function is called from hdd_hostapd_ioctl function when Driver
* get P2P_SET_NOA comand from wpa_supplicant using private ioctl
*
*LOGIC:
* Fill NoA Struct According to P2P Power save Option and Pass it to SME layer
*
*ASSUMPTIONS:
*
*
*NOTE:
*
* @param dev Pointer to net device structure
* @param command Pointer to command
*
* @return Status
*/
int hdd_setP2pNoa( struct net_device *dev, tANI_U8 *command )
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
VOS_STATUS status = VOS_STATUS_SUCCESS;
tP2pPsConfig NoA;
int count, duration, start_time;
char *param;
tANI_U8 ret = 0;
param = strnchr(command, strlen(command), ' ');
if (param == NULL)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: strnchr failed to find delimeter",__func__);
return -EINVAL;
}
param++;
ret = sscanf(param, "%d %d %d", &count, &start_time, &duration);
if (ret < 3)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: P2P_SET GO NoA: fail to read param "
"count=%d duration=%d interval=%d ",
__func__, count, start_time, duration);
return -EINVAL;
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: P2P_SET GO NoA: count=%d duration=%d interval=%d",
__func__, count, start_time, duration);
duration = MS_TO_MUS(duration);
/* PS Selection
* Periodic NoA (2)
* Single NOA (4)
*/
NoA.opp_ps = 0;
NoA.ctWindow = 0;
if (count == 1)
{
NoA.duration = 0;
NoA.single_noa_duration = duration;
NoA.psSelection = P2P_POWER_SAVE_TYPE_SINGLE_NOA;
}
else
{
NoA.duration = duration;
NoA.single_noa_duration = 0;
NoA.psSelection = P2P_POWER_SAVE_TYPE_PERIODIC_NOA;
}
NoA.interval = MS_TO_MUS(100);
NoA.count = count;
NoA.sessionid = pAdapter->sessionId;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d "
"interval %d count %d single noa duration %d "
"PsSelection %x", __func__, NoA.opp_ps,
NoA.ctWindow, NoA.duration, NoA.interval,
NoA.count, NoA.single_noa_duration,
NoA.psSelection);
sme_p2pSetPs(hHal, &NoA);
return status;
}
/**
* hdd_setP2pOpps
*
*FUNCTION:
* This function is called from hdd_hostapd_ioctl function when Driver
* get P2P_SET_PS comand from wpa_supplicant using private ioctl
*
*LOGIC:
* Fill NoA Struct According to P2P Power save Option and Pass it to SME layer
*
*ASSUMPTIONS:
*
*
*NOTE:
*
* @param dev Pointer to net device structure
* @param command Pointer to command
*
* @return Status
*/
int hdd_setP2pOpps( struct net_device *dev, tANI_U8 *command )
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
VOS_STATUS status = VOS_STATUS_SUCCESS;
tP2pPsConfig NoA;
char *param;
int legacy_ps, opp_ps, ctwindow;
tANI_U8 ret = 0;
param = strnchr(command, strlen(command), ' ');
if (param == NULL)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: strnchr failed to find delimeter",__func__);
return -EINVAL;
}
param++;
ret = sscanf(param, "%d %d %d", &legacy_ps, &opp_ps, &ctwindow);
if (ret < 3)
{
VOS_TRACE (VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: P2P_SET GO PS: fail to read param "
" legacy_ps=%d opp_ps=%d ctwindow=%d ",
__func__, legacy_ps, opp_ps, ctwindow);
return -EINVAL;
}
VOS_TRACE (VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: P2P_SET GO PS: legacy_ps=%d opp_ps=%d ctwindow=%d",
__func__, legacy_ps, opp_ps, ctwindow);
/* PS Selection
* Opportunistic Power Save (1)
*/
/* From wpa_cli user need to use separate command to set ctWindow and Opps
* When user want to set ctWindow during that time other parameters
* values are coming from wpa_supplicant as -1.
* Example : User want to set ctWindow with 30 then wpa_cli command :
* P2P_SET ctwindow 30
* Command Received at hdd_hostapd_ioctl is as below:
* P2P_SET_PS -1 -1 30 (legacy_ps = -1, opp_ps = -1, ctwindow = 30)
*/
if (ctwindow != -1)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Opportunistic Power Save is %s",
(TRUE == pAdapter->ops) ? "Enable" : "Disable" );
if (ctwindow != pAdapter->ctw)
{
pAdapter->ctw = ctwindow;
if(pAdapter->ops)
{
NoA.opp_ps = pAdapter->ops;
NoA.ctWindow = pAdapter->ctw;
NoA.duration = 0;
NoA.single_noa_duration = 0;
NoA.interval = 0;
NoA.count = 0;
NoA.psSelection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC;
NoA.sessionid = pAdapter->sessionId;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d "
"interval %d count %d single noa duration %d "
"PsSelection %x", __func__, NoA.opp_ps,
NoA.ctWindow, NoA.duration, NoA.interval,
NoA.count, NoA.single_noa_duration,
NoA.psSelection);
sme_p2pSetPs(hHal, &NoA);
}
return 0;
}
}
if (opp_ps != -1)
{
pAdapter->ops = opp_ps;
if ((opp_ps != -1) && (pAdapter->ctw))
{
NoA.opp_ps = opp_ps;
NoA.ctWindow = pAdapter->ctw;
NoA.duration = 0;
NoA.single_noa_duration = 0;
NoA.interval = 0;
NoA.count = 0;
NoA.psSelection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC;
NoA.sessionid = pAdapter->sessionId;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: P2P_PS_ATTR:oppPS %d ctWindow %d duration %d "
"interval %d count %d single noa duration %d "
"PsSelection %x", __func__, NoA.opp_ps,
NoA.ctWindow, NoA.duration, NoA.interval,
NoA.count, NoA.single_noa_duration,
NoA.psSelection);
sme_p2pSetPs(hHal, &NoA);
}
}
return status;
}
int hdd_setP2pPs( struct net_device *dev, void *msgData )
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
VOS_STATUS status = VOS_STATUS_SUCCESS;
tP2pPsConfig NoA;
p2p_app_setP2pPs_t *pappNoA = (p2p_app_setP2pPs_t *) msgData;
NoA.opp_ps = pappNoA->opp_ps;
NoA.ctWindow = pappNoA->ctWindow;
NoA.duration = pappNoA->duration;
NoA.interval = pappNoA->interval;
NoA.count = pappNoA->count;
NoA.single_noa_duration = pappNoA->single_noa_duration;
NoA.psSelection = pappNoA->psSelection;
NoA.sessionid = pAdapter->sessionId;
sme_p2pSetPs(hHal, &NoA);
return status;
}
static tANI_U8 wlan_hdd_get_session_type( enum nl80211_iftype type )
{
tANI_U8 sessionType;
switch( type )
{
case NL80211_IFTYPE_AP:
sessionType = WLAN_HDD_SOFTAP;
break;
case NL80211_IFTYPE_P2P_GO:
sessionType = WLAN_HDD_P2P_GO;
break;
case NL80211_IFTYPE_P2P_CLIENT:
sessionType = WLAN_HDD_P2P_CLIENT;
break;
case NL80211_IFTYPE_STATION:
sessionType = WLAN_HDD_INFRA_STATION;
break;
case NL80211_IFTYPE_MONITOR:
sessionType = WLAN_HDD_MONITOR;
break;
default:
sessionType = WLAN_HDD_INFRA_STATION;
break;
}
return sessionType;
}
/**
* hdd_close_all_adapters_per_mode() - close all adapters per mode
* @hdd_ctx: pointer to hdd context
* @mode: all adapters to be deleted in this mode
*
* Return: None
*/
static void
hdd_close_all_adapters_per_mode(hdd_context_t *hdd_ctx,
uint32_t mode)
{
hdd_adapter_t *adapter = hdd_get_adapter(hdd_ctx, mode);
while (adapter) {
hdd_stop_adapter(hdd_ctx, adapter, VOS_TRUE);
hdd_deinit_adapter(hdd_ctx, adapter, TRUE);
hdd_close_adapter(hdd_ctx, adapter, VOS_TRUE);
adapter = hdd_get_adapter(hdd_ctx, mode);
}
}
static void wlan_hdd_create_p2p_adapter(hdd_context_t *hdd_ctx,
tANI_U8 rtnl_held)
{
hdd_adapter_t *p2p_adapter;
p2p_adapter = hdd_open_adapter(hdd_ctx, WLAN_HDD_P2P_DEVICE, "p2p%d",
&hdd_ctx->p2pDeviceAddress.bytes[0],
rtnl_held);
if (!p2p_adapter)
hddLog(LOGE,
FL("Failed to do hdd_open_adapter for P2P Device Interface"));
}
/**
* wlan_hdd_add_monitor_check() - check for monitor intf and add if needed
* @hdd_ctx: pointer to hdd context
* @adapter: output pointer to hold created monitor adapter
* @type: type of the interface
* @name: name of the interface
*
* Return: 0 - on success
* 1 - on failure
*/
static int
wlan_hdd_add_monitor_check(hdd_context_t *hdd_ctx, hdd_adapter_t **adapter,
enum nl80211_iftype type, const char *name)
{
hdd_adapter_t *sta_adapter;
hdd_adapter_t *mon_adapter;
uint32_t i;
*adapter = NULL;
/*
* If add interface request is for monitor mode, then it can run in
* parallel with only one station interface.
* If there is no existing station interface return error
*/
if (type != NL80211_IFTYPE_MONITOR)
return 0;
if (!sme_IsFeatureSupportedByFW(STA_MONITOR_SCC)) {
hddLog(LOGE, FL("No FW support for STA + MON SCC"));
return -EINVAL;
}
if (hdd_ctx->no_of_open_sessions[VOS_MONITOR_MODE]) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: monitor mode already exists, only one is possible",
__func__);
return -EBUSY;
}
/* Ensure there is only one station interface */
if (hdd_ctx->no_of_open_sessions[VOS_STA_MODE] != 1) {
hddLog(LOGE,
FL("cannot add monitor mode, due to %u sta interfaces"),
hdd_ctx->no_of_open_sessions[VOS_STA_MODE]);
return -EINVAL;
}
sta_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!sta_adapter) {
hddLog(LOGE, FL("No station adapter"));
return -EINVAL;
}
/* delete all the other interfaces */
for (i = VOS_STA_SAP_MODE; i <= VOS_P2P_DEVICE; i++) {
if (i == VOS_FTM_MODE || i == VOS_MONITOR_MODE)
continue;
hdd_close_all_adapters_per_mode(hdd_ctx, i);
}
mon_adapter = hdd_open_adapter(hdd_ctx, WLAN_HDD_MONITOR, name,
wlan_hdd_get_intf_addr(hdd_ctx),
VOS_TRUE);
if (!mon_adapter) {
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: hdd_open_adapter failed",
__func__);
wlan_hdd_create_p2p_adapter(hdd_ctx, TRUE);
return -EINVAL;
}
*adapter = mon_adapter;
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
static struct wireless_dev *
__wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
static struct net_device *
__wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
#else
static int
__wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
#endif
{
hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy);
hdd_adapter_t *pAdapter = NULL;
hdd_scaninfo_t *pScanInfo = NULL;
int ret = 0;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return ERR_PTR(-EINVAL);
#else
return -EAGAIN;
#endif
}
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_ADD_VIRTUAL_INTF, NO_SESSION, type));
if (WLAN_HDD_P2P_CLIENT != wlan_hdd_get_session_type(type) &&
WLAN_HDD_INFRA_STATION != wlan_hdd_get_session_type(type) &&
hdd_get_adapter(pHddCtx, wlan_hdd_get_session_type(type)) != NULL)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: Interface type %d already exists. Two"
"interfaces of same type are not supported currently.",__func__, type);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return ERR_PTR(-EINVAL);
#else
return -EAGAIN;
#endif
}
if (pHddCtx->concurrency_mode == VOS_STA_MON) {
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: STA + MONITOR mode is in progress, cannot add new interface",
__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return ERR_PTR(-EINVAL);
#else
return -EBUSY;
#endif
}
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_INFRA_STATION);
pScanInfo = &pHddCtx->scan_info;
if ((pScanInfo != NULL) && (pAdapter != NULL) &&
(pHddCtx->scan_info.mScanPending))
{
hdd_abort_mac_scan(pHddCtx, pAdapter->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: Abort Scan while adding virtual interface",__func__);
}
ret = wlan_hdd_add_monitor_check(pHddCtx, &pAdapter, type, name);
if (ret) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return ERR_PTR(-EINVAL);
#else
return ret;
#endif
}
if (pAdapter)
goto return_adapter;
pAdapter = NULL;
if (pHddCtx->cfg_ini->isP2pDeviceAddrAdministrated &&
((NL80211_IFTYPE_P2P_GO == type) ||
(NL80211_IFTYPE_P2P_CLIENT == type)))
{
/* Generate the P2P Interface Address. this address must be
* different from the P2P Device Address.
*/
v_MACADDR_t p2pDeviceAddress = pHddCtx->p2pDeviceAddress;
p2pDeviceAddress.bytes[4] ^= 0x80;
pAdapter = hdd_open_adapter( pHddCtx,
wlan_hdd_get_session_type(type),
name, p2pDeviceAddress.bytes,
VOS_TRUE );
}
else
{
pAdapter = hdd_open_adapter( pHddCtx, wlan_hdd_get_session_type(type),
name, wlan_hdd_get_intf_addr(pHddCtx), VOS_TRUE );
}
if( NULL == pAdapter)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"%s: hdd_open_adapter failed",__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return ERR_PTR(-EINVAL);
#else
return -EINVAL;
#endif
}
if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO ||
type == NL80211_IFTYPE_AP)
{
/* Below function Notifies Mode change and
* If p2p session is detected then invokes functionality to
* Teardown TDLS links and disable offchannel if any. Since
* TDLS is not supported in case of concurrency.
*/
hddLog(LOG1, FL("Interface type = %d"), type);
hdd_tdls_notify_mode_change(pAdapter, pHddCtx);
}
return_adapter:
EXIT();
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
return pAdapter->dev->ieee80211_ptr;
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
return pAdapter->dev;
#else
return 0;
#endif
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
struct vif_params *params)
{
struct wireless_dev *wdev;
vos_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, &params->flags, params);
vos_ssr_unprotect(__func__);
return wdev;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) || \
defined(WITH_BACKPORTS)
/**
* wlan_hdd_add_virtual_intf() - Add virtual interface wrapper
* @wiphy: wiphy pointer
* @name: User-visible name of the interface
* @name_assign_type: the name of assign type of the netdev
* @nl80211_iftype: (virtual) interface types
* @flags: monitor mode configuration flags (not used)
* @vif_params: virtual interface parameters (not used)
*
* Return: the pointer of wireless dev, otherwise ERR_PTR.
*/
struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
unsigned char name_assign_type,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct wireless_dev *wdev;
vos_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, flags, params);
vos_ssr_unprotect(__func__);
return wdev;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0))
/**
* wlan_hdd_add_virtual_intf() - Add virtual interface wrapper
* @wiphy: wiphy pointer
* @name: User-visible name of the interface
* @nl80211_iftype: (virtual) interface types
* @flags: monitor mode configuration flags (not used)
* @vif_params: virtual interface parameters (not used)
*
* Return: the pointer of wireless dev, otherwise ERR_PTR.
*/
struct wireless_dev *wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
const char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct wireless_dev *wdev;
unsigned char name_assign_type = 0;
vos_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, flags, params);
vos_ssr_unprotect(__func__);
return wdev;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct wireless_dev* wlan_hdd_add_virtual_intf(
struct wiphy *wiphy, char *name, enum nl80211_iftype type,
u32 *flags, struct vif_params *params )
{
struct wireless_dev *wdev;
unsigned char name_assign_type = 0;
vos_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, (const char *)name,
name_assign_type,
type, flags, params);
vos_ssr_unprotect(__func__);
return wdev;
}
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38))
struct net_device* wlan_hdd_add_virtual_intf(struct wiphy *wiphy,
char *name,
enum nl80211_iftype type,
u32 *flags,
struct vif_params *params)
{
struct net_device *ndev;
unsigned char name_assign_type = 0;
vos_ssr_protect(__func__);
ndev = __wlan_hdd_add_virtual_intf(wiphy, (const char *)name, name_assign_type,
type, flags, params);
vos_ssr_unprotect(__func__);
return wdev;
}
#else
int wlan_hdd_add_virtual_intf(struct wiphy *wiphy, char *name,
enum nl80211_iftype type,
u32 *flags, struct vif_params *params)
{
int ret;
unsigned char name_assign_type = 0;
vos_ssr_protect(__func__);
ret = __wlan_hdd_add_virtual_intf(wiphy, (const char *)name, name_assign_type,
type, flags, params);
vos_ssr_unprotect(__func__);
return ret;
}
#endif
/**
* hdd_delete_adapter() - stop and close adapter
* @hdd_ctx: pointer to hdd context
* @adapter: adapter to be deleted
* @rtnl_held: rtnl lock held
*
* Rerurn: None
*/
static void
hdd_delete_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
tANI_U8 rtnl_held)
{
wlan_hdd_release_intf_addr(hdd_ctx, adapter->macAddressCurrent.bytes);
hdd_stop_adapter(hdd_ctx, adapter, VOS_TRUE);
hdd_deinit_adapter(hdd_ctx, adapter, TRUE);
hdd_close_adapter(hdd_ctx, adapter, rtnl_held);
}
/**
* wlan_hdd_del_monitor() - delete monitor interface
* @hdd_ctx: pointer to hdd context
* @adapter: adapter to be deleted
* @rtnl_held: rtnl lock held
*
* This function is invoked to delete monitor interface and also
* station interface if needed.
*
* Return: None
*/
static void
wlan_hdd_del_monitor(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
tANI_U8 rtnl_held)
{
hdd_adapter_t *monitor_adapter;
bool delete_station = false;
monitor_adapter = hdd_get_adapter(hdd_ctx, WLAN_HDD_MONITOR);
if (monitor_adapter != adapter)
delete_station = true;
/* delete the monitor adapter and re-create the p2p-dev adapter */
hdd_delete_adapter(hdd_ctx, monitor_adapter, rtnl_held);
wlan_hdd_create_p2p_adapter(hdd_ctx, rtnl_held);
if (delete_station)
hdd_delete_adapter(hdd_ctx, adapter, rtnl_held);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int __wlan_hdd_del_virtual_intf( struct wiphy *wiphy, struct wireless_dev *wdev )
#else
int __wlan_hdd_del_virtual_intf( struct wiphy *wiphy, struct net_device *dev )
#endif
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
struct net_device *dev = wdev->netdev;
#endif
hdd_context_t *pHddCtx = (hdd_context_t*) wiphy_priv(wiphy);
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
int status;
ENTER();
MTRACE(vos_trace(VOS_MODULE_ID_HDD,
TRACE_CODE_HDD_DEL_VIRTUAL_INTF,
pAdapter->sessionId, pAdapter->device_mode));
hddLog(VOS_TRACE_LEVEL_INFO, "%s: device_mode = %d",
__func__,pAdapter->device_mode);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
{
return status;
}
if (pHddCtx->concurrency_mode == VOS_STA_MON)
wlan_hdd_del_monitor(pHddCtx, pAdapter, TRUE);
else
hdd_delete_adapter(pHddCtx, pAdapter, TRUE);
EXIT();
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
int wlan_hdd_del_virtual_intf( struct wiphy *wiphy, struct wireless_dev *wdev )
#else
int wlan_hdd_del_virtual_intf( struct wiphy *wiphy, struct net_device *dev )
#endif
{
int ret;
vos_ssr_protect(__func__);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
ret = __wlan_hdd_del_virtual_intf(wiphy, wdev);
#else
ret = __wlan_hdd_del_virtual_intf(wiphy, dev);
#endif
vos_ssr_unprotect(__func__);
return ret;
}
void hdd_sendMgmtFrameOverMonitorIface( hdd_adapter_t *pMonAdapter,
tANI_U32 nFrameLength,
tANI_U8* pbFrames,
tANI_U8 frameType )
{
//Indicate a Frame over Monitor Intf.
int rxstat;
struct sk_buff *skb = NULL;
int needed_headroom = 0;
int flag = HDD_RX_FLAG_IV_STRIPPED | HDD_RX_FLAG_DECRYPTED |
HDD_RX_FLAG_MMIC_STRIPPED;
#ifdef WLAN_OPEN_SOURCE
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
hdd_context_t* pHddCtx = (hdd_context_t*)(pMonAdapter->pHddCtx);
#endif
#endif
hddLog( LOG1, FL("Indicate Frame over Monitor Intf"));
if (NULL == pbFrames)
{
hddLog(LOGE, FL("NULL frame pointer"));
return;
}
/* room for the radiotap header based on driver features
* 1 Byte for RADIO TAP Flag, 1 Byte padding and 2 Byte for
* RX flags.
* */
needed_headroom = sizeof(struct ieee80211_radiotap_header) + 4;
//alloc skb here
skb = alloc_skb(VPKT_SIZE_BUFFER, GFP_ATOMIC);
if (unlikely(NULL == skb))
{
hddLog( LOGW, FL("Unable to allocate skb"));
return;
}
skb_reserve(skb, VPKT_SIZE_BUFFER);
if (unlikely(skb_headroom(skb) < nFrameLength))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
"HDD [%d]: Insufficient headroom, "
"head[%pK], data[%pK], req[%d]",
__LINE__, skb->head, skb->data, nFrameLength);
kfree_skb(skb);
return ;
}
// actually push the data
memcpy(skb_push(skb, nFrameLength), pbFrames, nFrameLength);
/* prepend radiotap information */
if( 0 != hdd_wlan_add_rx_radiotap_hdr( skb, needed_headroom, flag ) )
{
hddLog( LOGE, FL("Not Able Add Radio Tap"));
//free skb
kfree_skb(skb);
return ;
}
skb_reset_mac_header( skb );
skb->dev = pMonAdapter->dev;
skb->protocol = eth_type_trans( skb, skb->dev );
skb->ip_summed = CHECKSUM_NONE;
#ifdef WLAN_OPEN_SOURCE
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
vos_wake_lock_timeout_release(&pHddCtx->rx_wake_lock,
HDD_WAKE_LOCK_DURATION,
WIFI_POWER_EVENT_WAKELOCK_HOLD_RX);
#endif
#endif
rxstat = netif_rx_ni(skb);
if( NET_RX_SUCCESS == rxstat )
{
hddLog( LOG1, FL("Success"));
}
else
hddLog( LOGE, FL("Failed %d"), rxstat);
return ;
}
void __hdd_indicate_mgmt_frame(hdd_adapter_t *pAdapter,
tANI_U32 nFrameLength,
tANI_U8* pbFrames,
tANI_U8 frameType,
tANI_U32 rxChan,
tANI_S8 rxRssi)
{
tANI_U16 freq;
tANI_U16 extend_time;
tANI_U8 type = 0;
tANI_U8 subType = 0;
tActionFrmType actionFrmType;
hdd_cfg80211_state_t *cfgState = NULL;
hdd_scaninfo_t *pScanInfo = NULL;
hdd_context_t *pHddCtx = NULL;
VOS_STATUS status;
hdd_remain_on_chan_ctx_t* pRemainChanCtx = NULL;
hddLog(VOS_TRACE_LEVEL_INFO, FL("Frame Type = %d Frame Length = %d"),
frameType, nFrameLength);
if (NULL == pAdapter)
{
hddLog(LOGE, FL("pAdapter is NULL"));
return;
}
if (0 == nFrameLength)
{
hddLog(LOGE, FL("Frame Length is Invalid ZERO"));
return;
}
if (NULL == pbFrames)
{
hddLog(LOGE, FL("pbFrames is NULL"));
return;
}
type = WLAN_HDD_GET_TYPE_FRM_FC(pbFrames[0]);
subType = WLAN_HDD_GET_SUBTYPE_FRM_FC(pbFrames[0]);
/* Get pAdapter from Destination mac address of the frame */
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType != SIR_MAC_MGMT_PROBE_REQ) &&
(nFrameLength > WLAN_HDD_80211_FRM_DA_OFFSET + VOS_MAC_ADDR_SIZE) &&
!vos_is_macaddr_broadcast(
(v_MACADDR_t *)&pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET]))
{
pAdapter = hdd_get_adapter_by_macaddr( WLAN_HDD_GET_CTX(pAdapter),
&pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET]);
if (NULL == pAdapter)
{
/* Under assumtion that we don't receive any action frame
* with BCST as destination we dropping action frame
*/
hddLog(VOS_TRACE_LEVEL_FATAL,"pAdapter for action frame is NULL Macaddr = "
MAC_ADDRESS_STR ,
MAC_ADDR_ARRAY(&pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET]));
hddLog(VOS_TRACE_LEVEL_FATAL, "%s: Frame Type = %d Frame Length = %d"
" subType = %d",__func__,frameType,nFrameLength,subType);
return;
}
}
if (NULL == pAdapter->dev)
{
hddLog( LOGE, FL("pAdapter->dev is NULL"));
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)
{
hddLog( LOGE, FL("pAdapter has invalid magic"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (NULL == pHddCtx)
{
hddLog(VOS_TRACE_LEVEL_FATAL,"%s: HDD Context Null Pointer", __func__);
return;
}
if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
(WLAN_HDD_P2P_GO == pAdapter->device_mode ))
{
hdd_adapter_t *pMonAdapter =
hdd_get_mon_adapter( WLAN_HDD_GET_CTX(pAdapter) );
if (NULL != pMonAdapter)
{
hddLog( LOG1, FL("Indicate Frame over Monitor Interface"));
hdd_sendMgmtFrameOverMonitorIface( pMonAdapter, nFrameLength,
pbFrames, frameType);
return;
}
}
//Channel indicated may be wrong. TODO
//Indicate an action frame.
if( rxChan <= MAX_NO_OF_2_4_CHANNELS )
{
freq = ieee80211_channel_to_frequency( rxChan,
HDD_NL80211_BAND_2GHZ);
}
else
{
freq = ieee80211_channel_to_frequency( rxChan,
HDD_NL80211_BAND_5GHZ);
}
cfgState = WLAN_HDD_GET_CFG_STATE_PTR( pAdapter );
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType == SIR_MAC_MGMT_ACTION) &&
(nFrameLength > WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1))
{
if(pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] == WLAN_HDD_PUBLIC_ACTION_FRAME)
{
// public action frame
if((WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + SIR_MAC_P2P_OUI_SIZE + 2 <
nFrameLength) &&
(pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET+1] ==
SIR_MAC_ACTION_VENDOR_SPECIFIC) &&
vos_mem_compare(&pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET+2], SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE))
// P2P action frames
{
actionFrmType = pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
hddLog(LOG1, "Rx Action Frame %u", actionFrmType);
#ifdef WLAN_FEATURE_P2P_DEBUG
if(actionFrmType >= MAX_P2P_ACTION_FRAME_TYPE)
{
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] unknown[%d] <--- OTA",
actionFrmType);
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,"[P2P] %s <--- OTA",
p2p_action_frame_type[actionFrmType]);
if( (actionFrmType == WLAN_HDD_PROV_DIS_REQ) &&
(globalP2PConnectionStatus == P2P_NOT_ACTIVE) )
{
globalP2PConnectionStatus = P2P_GO_NEG_PROCESS;
hddLog(LOGE,"[P2P State]Inactive state to "
"GO negotiation progress state");
}
else if( (actionFrmType == WLAN_HDD_GO_NEG_CNF) &&
(globalP2PConnectionStatus == P2P_GO_NEG_PROCESS) )
{
globalP2PConnectionStatus = P2P_GO_NEG_COMPLETED;
hddLog(LOGE,"[P2P State]GO negotiation progress to "
"GO negotiation completed state");
}
else if( (actionFrmType == WLAN_HDD_INVITATION_REQ) &&
(globalP2PConnectionStatus == P2P_NOT_ACTIVE) )
{
globalP2PConnectionStatus = P2P_GO_NEG_COMPLETED;
hddLog(LOGE,"[P2P State]Inactive state to GO negotiation"
" completed state Autonomous GO formation");
}
}
#endif
mutex_lock(&pHddCtx->roc_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx != NULL && VOS_TIMER_STATE_RUNNING
== vos_timer_getCurrentState(&pRemainChanCtx->hdd_remain_on_chan_timer))
{
if ( actionFrmType == WLAN_HDD_GO_NEG_REQ ||
actionFrmType == WLAN_HDD_GO_NEG_RESP ||
actionFrmType == WLAN_HDD_INVITATION_REQ ||
actionFrmType == WLAN_HDD_DEV_DIS_REQ ||
actionFrmType == WLAN_HDD_PROV_DIS_REQ )
{
hddLog( LOG1, "Extend RoC timer on reception of"
" Action Frame");
if ((actionFrmType == WLAN_HDD_GO_NEG_REQ)
|| (actionFrmType == WLAN_HDD_GO_NEG_RESP))
extend_time = 2 * ACTION_FRAME_DEFAULT_WAIT;
else
extend_time = ACTION_FRAME_DEFAULT_WAIT;
if (completion_done(&pAdapter->rem_on_chan_ready_event))
{
if(!VOS_IS_STATUS_SUCCESS(vos_timer_stop(
&pRemainChanCtx->hdd_remain_on_chan_timer)))
{
hddLog( LOGE, FL(
"Failed to stop hdd_remain_on_chan_timer"));
}
status = vos_timer_start(
&pRemainChanCtx->hdd_remain_on_chan_timer,
extend_time);
if (status != VOS_STATUS_SUCCESS)
{
hddLog( LOGE, "ROC timer start failed");
}
}
else
{
// Buffer Packet
if (pRemainChanCtx->action_pkt_buff.frame_length == 0)
{
pRemainChanCtx->action_pkt_buff.frame_length =
nFrameLength;
pRemainChanCtx->action_pkt_buff.freq = freq;
pRemainChanCtx->action_pkt_buff.frame_ptr
= vos_mem_malloc(nFrameLength);
vos_mem_copy(
pRemainChanCtx->action_pkt_buff.frame_ptr,
pbFrames, nFrameLength);
hddLog( LOGE,"%s:"
"Action Pkt Cached successfully !!!", __func__);
}
else
{
hddLog( LOGE,"%s:"
"Frames are pending. dropping frame !!!",
__func__);
}
mutex_unlock(&pHddCtx->roc_lock);
return;
}
}
}
if (pRemainChanCtx != NULL &&
vos_timer_is_initialized(
&cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer) &&
VOS_TIMER_STATE_RUNNING != vos_timer_getCurrentState(
&cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer))
hddLog( LOG1, "%s:"
"Rcvd action frame after timer expired ", __func__);
mutex_unlock(&pHddCtx->roc_lock);
if (((actionFrmType == WLAN_HDD_PROV_DIS_RESP) &&
(cfgState->actionFrmState == HDD_PD_REQ_ACK_PENDING)) ||
((actionFrmType == WLAN_HDD_GO_NEG_RESP) &&
(cfgState->actionFrmState == HDD_GO_NEG_REQ_ACK_PENDING)))
{
hddLog(LOG1, "%s: ACK_PENDING and But received RESP for Action frame ",
__func__);
cfgState->is_go_neg_ack_received = 1;
hdd_sendActionCnf(pAdapter, TRUE);
}
}
#ifdef FEATURE_WLAN_TDLS
else if(pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET+1] == WLAN_HDD_PUBLIC_ACTION_TDLS_DISC_RESP)
{
u8 *mac = &pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET+6];
hddLog(VOS_TRACE_LEVEL_INFO,"[TDLS] TDLS Discovery Response," MAC_ADDRESS_STR " RSSI[%d] <--- OTA",
MAC_ADDR_ARRAY(mac),rxRssi);
wlan_hdd_tdls_set_rssi(pAdapter, mac, rxRssi);
wlan_hdd_tdls_recv_discovery_resp(pAdapter, mac);
}
#endif
}
pScanInfo = &pHddCtx->scan_info;
if ((pScanInfo != NULL) && (pHddCtx->scan_info.mScanPending))
{
hddLog(LOGE,"Action frame received when Scanning is in"
" progress. Abort Scan.");
hdd_abort_mac_scan(pAdapter->pHddCtx,
pScanInfo->sessionId,
eCSR_SCAN_ABORT_DEFAULT);
}
if(pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] == WLAN_HDD_TDLS_ACTION_FRAME)
{
actionFrmType = pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET+1];
if(actionFrmType >= MAX_TDLS_ACTION_FRAME_TYPE)
{
hddLog(VOS_TRACE_LEVEL_INFO,"[TDLS] Action type[%d] <--- OTA",
actionFrmType);
}
else
{
hddLog(VOS_TRACE_LEVEL_INFO,"[TDLS] %s <--- OTA",
tdls_action_frame_type[actionFrmType]);
}
vos_tdls_tx_rx_mgmt_event(SIR_MAC_ACTION_TDLS,
SIR_MAC_ACTION_RX, SIR_MAC_MGMT_ACTION,
actionFrmType, &pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET+6]);
}
if((pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] == WLAN_HDD_QOS_ACTION_FRAME)&&
(pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET+1] == WLAN_HDD_QOS_MAP_CONFIGURE) )
{
sme_UpdateDSCPtoUPMapping(pHddCtx->hHal,
pAdapter->hddWmmDscpToUpMap, pAdapter->sessionId);
}
}
//Indicate Frame Over Normal Interface
hddLog( LOG1, FL("Indicate Frame over NL80211 Interface"));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr, freq, rxRssi * 100, pbFrames,
nFrameLength, NL80211_RXMGMT_FLAG_ANSWERED);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,0))
cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr, freq, rxRssi * 100, pbFrames,
nFrameLength, NL80211_RXMGMT_FLAG_ANSWERED, GFP_ATOMIC);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0))
cfg80211_rx_mgmt( pAdapter->dev->ieee80211_ptr, freq, rxRssi * 100,
pbFrames, nFrameLength,
GFP_ATOMIC );
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
cfg80211_rx_mgmt( pAdapter->dev, freq, rxRssi * 100,
pbFrames, nFrameLength,
GFP_ATOMIC );
#else
cfg80211_rx_mgmt( pAdapter->dev, freq,
pbFrames, nFrameLength,
GFP_ATOMIC );
#endif //LINUX_VERSION_CODE
}
/*
* ieee80211_add_rx_radiotap_header - add radiotap header
*/
static int hdd_wlan_add_rx_radiotap_hdr (
struct sk_buff *skb, int rtap_len, int flag )
{
u8 rtap_temp[20] = {0};
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u16 rx_flags = 0;
rthdr = (struct ieee80211_radiotap_header *)(&rtap_temp[0]);
/* radiotap header, set always present flags */
rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
rthdr->it_len = cpu_to_le16(rtap_len);
pos = (unsigned char *) (rthdr + 1);
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_FLAGS */
*pos = 0;
pos++;
/* IEEE80211_RADIOTAP_RX_FLAGS: Length 2 Bytes */
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (u8 *)rthdr) & 1)
pos++;
put_unaligned_le16(rx_flags, pos);
pos += 2;
// actually push the data
memcpy(skb_push(skb, rtap_len), &rtap_temp[0], rtap_len);
return 0;
}
static void hdd_wlan_tx_complete( hdd_adapter_t* pAdapter,
hdd_cfg80211_state_t* cfgState,
tANI_BOOLEAN actionSendSuccess )
{
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
struct sk_buff *skb = cfgState->skb;
#ifdef WLAN_OPEN_SOURCE
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
hdd_context_t *pHddCtx = (hdd_context_t*)(pAdapter->pHddCtx);
#endif
#endif
/* 2 Byte for TX flags and 1 Byte for Retry count */
u32 rtHdrLen = sizeof(*rthdr) + 3;
u8 *data;
/* We have to return skb with Data starting with MAC header. We have
* copied SKB data starting with MAC header to cfgState->buf. We will pull
* entire skb->len from skb and then we will push cfgState->buf to skb
* */
if( NULL == skb_pull(skb, skb->len) )
{
hddLog( LOGE, FL("Not Able to Pull %d byte from skb"), skb->len);
kfree_skb(cfgState->skb);
return;
}
data = skb_push( skb, cfgState->len );
if (data == NULL)
{
hddLog( LOGE, FL("Not Able to Push %zu byte to skb"), cfgState->len);
kfree_skb( cfgState->skb );
return;
}
memcpy( data, cfgState->buf, cfgState->len );
/* send frame to monitor interfaces now */
if( skb_headroom(skb) < rtHdrLen )
{
hddLog( LOGE, FL("No headroom for rtap header"));
kfree_skb(cfgState->skb);
return;
}
rthdr = (struct ieee80211_radiotap_header*) skb_push( skb, rtHdrLen );
memset( rthdr, 0, rtHdrLen );
rthdr->it_len = cpu_to_le16( rtHdrLen );
rthdr->it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
(1 << IEEE80211_RADIOTAP_DATA_RETRIES)
);
pos = (unsigned char *)( rthdr+1 );
// Fill TX flags
*pos = actionSendSuccess;
pos += 2;
// Fill retry count
*pos = 0;
pos++;
skb_set_mac_header( skb, 0 );
skb->ip_summed = CHECKSUM_NONE;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = htons(ETH_P_802_2);
memset( skb->cb, 0, sizeof( skb->cb ) );
#ifdef WLAN_OPEN_SOURCE
#ifdef WLAN_FEATURE_HOLD_RX_WAKELOCK
vos_wake_lock_timeout_release(&pHddCtx->rx_wake_lock,
HDD_WAKE_LOCK_DURATION,
WIFI_POWER_EVENT_WAKELOCK_HOLD_RX);
#endif
#endif
if (in_interrupt())
netif_rx( skb );
else
netif_rx_ni( skb );
/* Enable Queues which we have disabled earlier */
hddLog(VOS_TRACE_LEVEL_INFO, FL("Enabling queues"));
netif_tx_start_all_queues( pAdapter->dev );
}
void __hdd_p2p_roc_work_queue(struct work_struct *work)
{
hdd_adapter_t *pAdapter = container_of(to_delayed_work(work), hdd_adapter_t, roc_work);
hddLog( VOS_TRACE_LEVEL_INFO, FL("%s: "), __func__);
wlan_hdd_p2p_start_remain_on_channel(pAdapter);
return;
}
void hdd_p2p_roc_work_queue(struct work_struct *work)
{
vos_ssr_protect(__func__);
__hdd_p2p_roc_work_queue(work);
vos_ssr_unprotect(__func__);
return;
}