blob: 48e4c28c90e91fa9538017c29911a2e36f6a6f15 [file] [log] [blame]
/*
* Copyright (c) 2012-2017 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_qos_api.h"
#include "wlan_hdd_p2p.h"
#include "sap_api.h"
#include "wlan_hdd_main.h"
#include "qdf_trace.h"
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/etherdevice.h>
#include <net/ieee80211_radiotap.h>
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_trace.h"
#include "qdf_types.h"
#include "qdf_trace.h"
#include "cds_sched.h"
#include "wlan_policy_mgr_api.h"
#include "cds_utils.h"
#ifdef CONVERGED_P2P_ENABLE
#include "wlan_p2p_public_struct.h"
#include "wlan_p2p_ucfg_api.h"
#include "wlan_cfg80211_p2p.h"
#else
#include "wma_api.h"
#endif
/* Ms to Time Unit Micro Sec */
#define MS_TO_TU_MUS(x) ((x) * 1024)
#define MAX_MUS_VAL (INT_MAX / 1024)
#ifndef CONVERGED_P2P_ENABLE
static uint8_t *hdd_get_action_string(uint16_t 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_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";
}
}
#endif
#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
*/
enum p2p_connection_status global_p2p_connection_status = 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"};
#ifndef CONVERGED_P2P_ENABLE
static bool wlan_hdd_is_type_p2p_action(const u8 *buf)
{
const u8 *ouiPtr;
if (buf[WLAN_HDD_PUBLIC_ACTION_FRAME_CATEGORY_OFFSET] !=
WLAN_HDD_PUBLIC_ACTION_FRAME)
return false;
if (buf[WLAN_HDD_PUBLIC_ACTION_FRAME_ACTION_OFFSET] !=
WLAN_HDD_VENDOR_SPECIFIC_ACTION)
return false;
ouiPtr = &buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_OFFSET];
if (WPA_GET_BE24(ouiPtr) != WLAN_HDD_WFA_OUI)
return false;
if (buf[WLAN_HDD_PUBLIC_ACTION_FRAME_OUI_TYPE_OFFSET] !=
WLAN_HDD_WFA_P2P_OUI_TYPE)
return false;
return true;
}
static bool hdd_p2p_is_action_type_rsp(const u8 *buf)
{
enum action_frm_type actionFrmType;
if (wlan_hdd_is_type_p2p_action(buf)) {
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 true;
}
return false;
}
#endif
static
QDF_STATUS wlan_hdd_remain_on_channel_callback(tHalHandle hHal, void *pCtx,
QDF_STATUS status, uint32_t scan_id)
{
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;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
enum rem_on_channel_request_type req_type;
if (!hdd_ctx) {
hdd_err("Invalid HDD context");
return QDF_STATUS_E_FAILURE;
}
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx == NULL) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_warn("No Rem on channel pending for which Rsp is received");
return QDF_STATUS_SUCCESS;
}
hdd_debug("Received remain on channel rsp");
if (qdf_mc_timer_stop(&pRemainChanCtx->hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
if (qdf_mc_timer_destroy(&pRemainChanCtx->hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to destroy hdd_remain_on_chan_timer");
cfgState->remain_on_chan_ctx = NULL;
/*
* Resetting the roc in progress early ensures that the subsequent
* roc requests are immediately processed without being queued
*/
pAdapter->is_roc_inprogress = false;
qdf_runtime_pm_allow_suspend(hdd_ctx->runtime_context.roc);
/*
* If the allow suspend is done later, the scheduled roc wil prevent
* the system from going into suspend and immediately this logic
* will allow the system to go to suspend breaking the exising logic.
* Basically, the system must not go into suspend while roc is in
* progress.
*/
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
if (REMAIN_ON_CHANNEL_REQUEST == pRemainChanCtx->rem_on_chan_request) {
if (cfgState->buf)
hdd_debug("Yet to rcv an ack for one of the tx pkt");
cfg80211_remain_on_channel_expired(
pRemainChanCtx->dev->
ieee80211_ptr,
pRemainChanCtx->
cookie,
&pRemainChanCtx->chan,
GFP_KERNEL);
pAdapter->last_roc_ts =
(uint64_t)qdf_mc_timer_get_system_time();
}
req_type = pRemainChanCtx->rem_on_chan_request;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
if ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)
) {
uint8_t sessionId = pAdapter->sessionId;
if (REMAIN_ON_CHANNEL_REQUEST == req_type) {
sme_deregister_mgmt_frame(hHal, sessionId,
(SIR_MAC_MGMT_FRAME << 2) |
(SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0);
}
} else if ((QDF_SAP_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == pAdapter->device_mode)
) {
wlansap_de_register_mgmt_frame(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
(SIR_MAC_MGMT_FRAME << 2) |
(SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0);
}
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
if (pRemainChanCtx) {
if (pRemainChanCtx->action_pkt_buff.frame_ptr != NULL
&& pRemainChanCtx->action_pkt_buff.frame_length != 0) {
qdf_mem_free(pRemainChanCtx->action_pkt_buff.frame_ptr);
pRemainChanCtx->action_pkt_buff.frame_ptr = NULL;
pRemainChanCtx->action_pkt_buff.frame_length = 0;
}
}
qdf_mem_free(pRemainChanCtx);
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
complete(&pAdapter->cancel_rem_on_chan_var);
if (QDF_STATUS_SUCCESS != status)
complete(&pAdapter->rem_on_chan_ready_event);
/* If we schedule work queue to start new RoC before completing
* cancel_rem_on_chan_var then the work queue may immediately get
* scheduled and update cfgState->remain_on_chan_ctx which is referred
* in mgmt_tx. Due to this update the the mgmt_tx may extend the roc
* which was already completed. This will lead to mgmt tx failure.
* Always schedule below work queue only after completing the
* cancel_rem_on_chan_var event.
*/
schedule_delayed_work(&hdd_ctx->roc_req_work, 0);
return QDF_STATUS_SUCCESS;
}
#ifdef CONVERGED_P2P_ENABLE
void wlan_hdd_cancel_existing_remain_on_channel(hdd_adapter_t *pAdapter)
{
QDF_STATUS status;
if (!pAdapter) {
hdd_err("null adapter");
return;
}
status = ucfg_p2p_cleanup_roc(pAdapter->hdd_vdev);
hdd_debug("status:%d", status);
}
#else
void 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;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
unsigned long rc;
uint32_t roc_scan_id;
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
if (cfgState->remain_on_chan_ctx != NULL) {
hdd_debug("Cancel Existing Remain on Channel");
if (QDF_TIMER_STATE_RUNNING == qdf_mc_timer_get_current_state(
&cfgState->remain_on_chan_ctx->hdd_remain_on_chan_timer)) {
if (qdf_mc_timer_stop(&cfgState->remain_on_chan_ctx->
hdd_remain_on_chan_timer) != QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
}
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress ==
true) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_err("ROC timer cancellation in progress wait for completion");
rc = wait_for_completion_timeout(&pAdapter->
cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc)
hdd_err("wait on cancel_rem_on_chan_var timed out");
return;
}
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = true;
roc_scan_id = pRemainChanCtx->scan_id;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
/* 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.
*/
rc = wait_for_completion_timeout(&pAdapter->
rem_on_chan_ready_event,
msecs_to_jiffies
(WAIT_REM_CHAN_READY));
if (!rc) {
hdd_err("timeout waiting for remain on channel ready indication");
cds_flush_logs(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_HDD_TIME_OUT,
true, false);
}
INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var);
/* 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 ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)
) {
sme_cancel_remain_on_channel(WLAN_HDD_GET_HAL_CTX
(pAdapter),
pAdapter->sessionId, roc_scan_id);
} else if ((QDF_SAP_MODE == pAdapter->device_mode)
|| (QDF_P2P_GO_MODE == pAdapter->device_mode)
) {
wlansap_cancel_remain_on_channel(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), roc_scan_id);
}
rc = wait_for_completion_timeout(&pAdapter->
cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc)
hdd_err("timeout waiting for cancel remain on channel ready indication");
qdf_runtime_pm_allow_suspend(hdd_ctx->runtime_context.roc);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
} else
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
#endif
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 (QDF_P2P_GO_MODE != 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 {
hdd_debug("Cannot Cancel Existing Remain on Channel");
status = -EBUSY;
}
}
}
return status;
}
/**
* wlan_hdd_cancel_pending_roc() - Cancel pending roc
* @adapter: HDD adapter
*
* Cancels any pending remain on channel request
*
* Return: None
*/
#ifndef CONVERGED_P2P_ENABLE
static void wlan_hdd_cancel_pending_roc(hdd_adapter_t *adapter)
{
hdd_remain_on_chan_ctx_t *roc_ctx;
unsigned long rc;
hdd_cfg80211_state_t *cfg_state = WLAN_HDD_GET_CFG_STATE_PTR(adapter);
uint32_t roc_scan_id;
hdd_debug("ROC completion is not received !!!");
mutex_lock(&cfg_state->remain_on_chan_ctx_lock);
roc_ctx = cfg_state->remain_on_chan_ctx;
if (!roc_ctx) {
mutex_unlock(&cfg_state->remain_on_chan_ctx_lock);
hdd_debug("roc_ctx is NULL, No pending RoC");
return;
}
if (roc_ctx->hdd_remain_on_chan_cancel_in_progress) {
mutex_unlock(&cfg_state->remain_on_chan_ctx_lock);
hdd_debug("roc cancel already in progress");
/*
* Since a cancel roc is already issued and is
* in progress, we need not send another
* cancel roc again. Instead we can just wait
* for cancel roc completion
*/
goto wait;
}
roc_scan_id = roc_ctx->scan_id;
mutex_unlock(&cfg_state->remain_on_chan_ctx_lock);
if (adapter->device_mode == QDF_P2P_GO_MODE) {
wlansap_cancel_remain_on_channel((WLAN_HDD_GET_CTX
(adapter))->pcds_context, roc_scan_id);
} else if (adapter->device_mode == QDF_P2P_CLIENT_MODE
|| adapter->device_mode ==
QDF_P2P_DEVICE_MODE) {
sme_cancel_remain_on_channel(WLAN_HDD_GET_HAL_CTX
(adapter),
adapter->sessionId, roc_scan_id);
}
wait:
rc = wait_for_completion_timeout(&adapter->cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc) {
hdd_err("Timeout occurred while waiting for RoC Cancellation");
mutex_lock(&cfg_state->remain_on_chan_ctx_lock);
roc_ctx = cfg_state->remain_on_chan_ctx;
if (roc_ctx != NULL) {
cfg_state->remain_on_chan_ctx = NULL;
if (qdf_mc_timer_stop(&roc_ctx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
if (qdf_mc_timer_destroy(
&roc_ctx->hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to destroy hdd_remain_on_chan_timer");
if (roc_ctx->action_pkt_buff.frame_ptr != NULL
&& roc_ctx->action_pkt_buff.frame_length != 0) {
qdf_mem_free(
roc_ctx->action_pkt_buff.frame_ptr);
roc_ctx->action_pkt_buff.frame_ptr = NULL;
roc_ctx->action_pkt_buff.frame_length = 0;
}
qdf_mem_free(roc_ctx);
adapter->is_roc_inprogress = false;
}
mutex_unlock(&cfg_state->remain_on_chan_ctx_lock);
}
}
#endif
/* Clean up RoC context at hdd_stop_adapter*/
#ifdef CONVERGED_P2P_ENABLE
void wlan_hdd_cleanup_remain_on_channel_ctx(hdd_adapter_t *pAdapter)
{
QDF_STATUS status;
if (!pAdapter) {
hdd_err("null adapter");
return;
}
status = ucfg_p2p_cleanup_roc(pAdapter->hdd_vdev);
hdd_debug("status:%d", status);
}
#else
void wlan_hdd_cleanup_remain_on_channel_ctx(hdd_adapter_t *pAdapter)
{
uint8_t retry = 0;
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
while (pAdapter->is_roc_inprogress) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("ROC in progress for session %d!!!",
pAdapter->sessionId);
msleep(500);
if (retry++ > 3) {
wlan_hdd_cancel_pending_roc(pAdapter);
/* hold the lock before break from the loop */
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
break;
}
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
} /* end of while */
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
#endif
static void wlan_hdd_remain_on_chan_timeout(void *data)
{
hdd_adapter_t *pAdapter = (hdd_adapter_t *) data;
hdd_remain_on_chan_ctx_t *pRemainChanCtx;
hdd_cfg80211_state_t *cfgState;
hdd_context_t *hdd_ctx;
uint32_t roc_scan_id;
if ((NULL == pAdapter) ||
(WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
hdd_err("pAdapter is invalid %p !!!", pAdapter);
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (NULL == pRemainChanCtx) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_err("No Remain on channel is pending");
return;
}
if (true == pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_err("Cancellation already in progress");
return;
}
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = true;
roc_scan_id = pRemainChanCtx->scan_id;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("Cancel Remain on Channel on timeout");
if ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)
) {
sme_cancel_remain_on_channel(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, roc_scan_id);
} else if ((QDF_SAP_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == pAdapter->device_mode)
) {
wlansap_cancel_remain_on_channel(
(WLAN_HDD_GET_CTX(pAdapter))->pcds_context,
roc_scan_id);
}
hdd_tdls_notify_p2p_roc(hdd_ctx, P2P_ROC_END);
qdf_runtime_pm_allow_suspend(hdd_ctx->runtime_context.roc);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
}
static int wlan_hdd_execute_remain_on_channel(hdd_adapter_t *pAdapter,
hdd_remain_on_chan_ctx_t *pRemainChanCtx)
{
hdd_cfg80211_state_t *cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter_temp;
QDF_STATUS status;
bool isGoPresent = false;
unsigned int duration;
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
if (pAdapter->is_roc_inprogress == true) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_err("remain on channel request is in execution");
return -EBUSY;
}
cfgState->remain_on_chan_ctx = pRemainChanCtx;
cfgState->current_freq = pRemainChanCtx->chan.center_freq;
pAdapter->is_roc_inprogress = true;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
/* Initialize Remain on chan timer */
qdf_status =
qdf_mc_timer_init(&pRemainChanCtx->hdd_remain_on_chan_timer,
QDF_TIMER_TYPE_SW,
wlan_hdd_remain_on_chan_timeout, pAdapter);
if (qdf_status != QDF_STATUS_SUCCESS) {
hdd_err("Not able to initialize remain_on_chan timer");
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
cfgState->remain_on_chan_ctx = NULL;
pAdapter->is_roc_inprogress = false;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
qdf_mem_free(pRemainChanCtx);
return -EINVAL;
}
status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
while (NULL != pAdapterNode && QDF_STATUS_SUCCESS == status) {
pAdapter_temp = pAdapterNode->pAdapter;
if (pAdapter_temp->device_mode == QDF_P2P_GO_MODE)
isGoPresent = true;
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
/* Extending duration for proactive extension logic for RoC */
duration = pRemainChanCtx->duration;
if (duration < HDD_P2P_MAX_ROC_DURATION) {
if (isGoPresent == true)
duration *= P2P_ROC_DURATION_MULTIPLIER_GO_PRESENT;
else
duration *= P2P_ROC_DURATION_MULTIPLIER_GO_ABSENT;
}
hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
qdf_runtime_pm_prevent_suspend(pHddCtx->runtime_context.roc);
INIT_COMPLETION(pAdapter->rem_on_chan_ready_event);
/* call sme API to start remain on channel. */
if ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)
) {
uint8_t sessionId = pAdapter->sessionId;
/* call sme API to start remain on channel. */
if (QDF_STATUS_SUCCESS != sme_remain_on_channel(
WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId,
pRemainChanCtx->chan.hw_value, duration,
wlan_hdd_remain_on_channel_callback,
pAdapter,
(pRemainChanCtx->rem_on_chan_request ==
REMAIN_ON_CHANNEL_REQUEST) ? true : false,
&pRemainChanCtx->scan_id)) {
hdd_err("sme_remain_on_channel failed");
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pAdapter->is_roc_inprogress = false;
pRemainChanCtx = cfgState->remain_on_chan_ctx;
hdd_debug("Freeing ROC ctx cfgState->remain_on_chan_ctx=%p",
cfgState->remain_on_chan_ctx);
if (pRemainChanCtx) {
if (qdf_mc_timer_destroy(
&pRemainChanCtx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to destroy hdd_remain_on_chan_timer");
qdf_mem_free(pRemainChanCtx);
cfgState->remain_on_chan_ctx = NULL;
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
qdf_runtime_pm_allow_suspend(pHddCtx->runtime_context.
roc);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
if (REMAIN_ON_CHANNEL_REQUEST ==
pRemainChanCtx->rem_on_chan_request) {
if (QDF_STATUS_SUCCESS != sme_register_mgmt_frame(
WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId,
(SIR_MAC_MGMT_FRAME << 2) |
(SIR_MAC_MGMT_PROBE_REQ << 4),
NULL, 0))
hdd_err("sme_register_mgmt_frame failed");
}
} else if ((QDF_SAP_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == pAdapter->device_mode)) {
/* call sme API to start remain on channel. */
if (QDF_STATUS_SUCCESS != wlansap_remain_on_channel(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
pRemainChanCtx->chan.hw_value,
duration, wlan_hdd_remain_on_channel_callback,
pAdapter, &pRemainChanCtx->scan_id)) {
hdd_err("wlansap_remain_on_channel failed");
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pAdapter->is_roc_inprogress = false;
pRemainChanCtx = cfgState->remain_on_chan_ctx;
hdd_debug("Freeing ROC ctx cfgState->remain_on_chan_ctx=%p",
cfgState->remain_on_chan_ctx);
if (pRemainChanCtx) {
if (qdf_mc_timer_destroy(
&pRemainChanCtx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to destroy hdd_remain_on_chan_timer");
qdf_mem_free(pRemainChanCtx);
cfgState->remain_on_chan_ctx = NULL;
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
qdf_runtime_pm_allow_suspend(pHddCtx->runtime_context.
roc);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
if (QDF_STATUS_SUCCESS != wlansap_register_mgmt_frame(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
(SIR_MAC_MGMT_FRAME << 2) |
(SIR_MAC_MGMT_PROBE_REQ << 4), NULL, 0)) {
hdd_err("wlansap_register_mgmt_frame return fail");
wlansap_cancel_remain_on_channel(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
pRemainChanCtx->scan_id);
qdf_runtime_pm_allow_suspend(pHddCtx->runtime_context.
roc);
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
return -EINVAL;
}
}
hdd_tdls_notify_p2p_roc(pHddCtx, P2P_ROC_START);
return 0;
}
/**
* wlan_hdd_roc_request_enqueue() - enqueue remain on channel request
* @adapter: Pointer to the adapter
* @remain_chan_ctx: Pointer to the remain on channel context
*
* Return: 0 on success, error number otherwise
*/
#ifndef CONVERGED_P2P_ENABLE
static int wlan_hdd_roc_request_enqueue(hdd_adapter_t *adapter,
hdd_remain_on_chan_ctx_t *remain_chan_ctx)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
hdd_roc_req_t *hdd_roc_req;
QDF_STATUS status;
/*
* "Driver is busy" OR "there is already RoC request inside the queue"
* so enqueue this RoC Request and execute sequentially later.
*/
hdd_roc_req = qdf_mem_malloc(sizeof(*hdd_roc_req));
if (NULL == hdd_roc_req) {
hdd_err("malloc failed for roc req context");
return -ENOMEM;
}
hdd_roc_req->pAdapter = adapter;
hdd_roc_req->pRemainChanCtx = remain_chan_ctx;
/* Enqueue this RoC request */
qdf_spin_lock(&hdd_ctx->hdd_roc_req_q_lock);
status = qdf_list_insert_back(&hdd_ctx->hdd_roc_req_q,
&hdd_roc_req->node);
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Not able to enqueue RoC Req context");
qdf_mem_free(hdd_roc_req);
return -EINVAL;
}
return 0;
}
#endif
/**
* wlan_hdd_indicate_roc_drop() - Indicate roc drop to userspace
* @adapter: HDD adapter
* @ctx: Remain on channel context
*
* Send remain on channel ready and cancel event for the queued
* roc that is being dropped. This will ensure that the userspace
* will send more roc requests. If this drop is not indicated to
* userspace, subsequent roc will not be sent to the driver since
* the userspace times out waiting for the remain on channel ready
* event.
*
* Return: None
*/
static void wlan_hdd_indicate_roc_drop(hdd_adapter_t *adapter,
hdd_remain_on_chan_ctx_t *ctx)
{
hdd_debug("indicate roc drop to userspace");
cfg80211_ready_on_channel(
adapter->dev->ieee80211_ptr,
(uintptr_t)ctx,
&ctx->chan,
ctx->duration, GFP_KERNEL);
cfg80211_remain_on_channel_expired(
ctx->dev->ieee80211_ptr,
ctx->cookie,
&ctx->chan,
GFP_KERNEL);
}
/**
* wlan_hdd_roc_request_dequeue() - dequeue remain on channel request
* @work: Pointer to work queue struct
*
* Return: none
*/
void wlan_hdd_roc_request_dequeue(struct work_struct *work)
{
QDF_STATUS status;
int ret = 0;
hdd_roc_req_t *hdd_roc_req;
hdd_context_t *hdd_ctx =
container_of(work, hdd_context_t, roc_req_work.work);
hdd_debug("going to dequeue roc");
if (0 != (wlan_hdd_validate_context(hdd_ctx)))
return;
/*
* The queued roc requests is dequeued and processed one at a time.
* Callback 'wlan_hdd_remain_on_channel_callback' ensures
* that any pending roc in the queue will be scheduled
* on the current roc completion by scheduling the work queue.
*/
qdf_spin_lock(&hdd_ctx->hdd_roc_req_q_lock);
if (list_empty(&hdd_ctx->hdd_roc_req_q.anchor)) {
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
return;
}
status = qdf_list_remove_front(&hdd_ctx->hdd_roc_req_q,
(qdf_list_node_t **) &hdd_roc_req);
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
if (QDF_STATUS_SUCCESS != status) {
hdd_debug("unable to remove roc element from list");
return;
}
ret = wlan_hdd_execute_remain_on_channel(
hdd_roc_req->pAdapter,
hdd_roc_req->pRemainChanCtx);
if (ret == -EBUSY) {
hdd_err("dropping RoC request");
wlan_hdd_indicate_roc_drop(hdd_roc_req->pAdapter,
hdd_roc_req->pRemainChanCtx);
qdf_mem_free(hdd_roc_req->pRemainChanCtx);
}
qdf_mem_free(hdd_roc_req);
}
#ifndef CONVERGED_P2P_ENABLE
static int wlan_hdd_request_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
unsigned int duration,
u64 *cookie,
enum rem_on_channel_request_type
request_type)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx;
hdd_remain_on_chan_ctx_t *pRemainChanCtx;
bool isBusy = false;
uint32_t size = 0;
hdd_adapter_t *sta_adapter;
int ret;
int status = 0;
hdd_debug("Device_mode %s(%d)",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
hdd_debug("chan(hw_val)0x%x chan(centerfreq) %d, duration %d",
chan->hw_value, chan->center_freq, duration);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return ret;
if (hdd_is_connection_in_progress(NULL, NULL)) {
hdd_debug("Connection is in progress");
isBusy = true;
}
pRemainChanCtx = qdf_mem_malloc(sizeof(hdd_remain_on_chan_ctx_t));
if (NULL == pRemainChanCtx) {
hdd_err("Not able to allocate memory for Channel context");
return -ENOMEM;
}
qdf_mem_copy(&pRemainChanCtx->chan, chan,
sizeof(struct ieee80211_channel));
pRemainChanCtx->duration = duration;
pRemainChanCtx->dev = dev;
*cookie = (uintptr_t) pRemainChanCtx;
pRemainChanCtx->cookie = *cookie;
pRemainChanCtx->rem_on_chan_request = request_type;
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;
if (REMAIN_ON_CHANNEL_REQUEST == request_type) {
sta_adapter = hdd_get_adapter(pHddCtx, QDF_STA_MODE);
if ((NULL != sta_adapter) &&
hdd_conn_is_connected(
WLAN_HDD_GET_STATION_CTX_PTR(sta_adapter))) {
if (pAdapter->last_roc_ts != 0 &&
(((uint64_t)qdf_mc_timer_get_system_time() -
pAdapter->last_roc_ts) <
pHddCtx->config->p2p_listen_defer_interval)) {
if (pRemainChanCtx->duration > HDD_P2P_MAX_ROC_DURATION)
pRemainChanCtx->duration =
HDD_P2P_MAX_ROC_DURATION;
wlan_hdd_roc_request_enqueue(pAdapter, pRemainChanCtx);
schedule_delayed_work(&pHddCtx->roc_req_work,
msecs_to_jiffies(
pHddCtx->config->p2p_listen_defer_interval));
hdd_debug("Defer interval is %hu, pAdapter %p",
pHddCtx->config->p2p_listen_defer_interval,
pAdapter);
return 0;
}
}
}
qdf_spin_lock(&pHddCtx->hdd_roc_req_q_lock);
size = qdf_list_size(&(pHddCtx->hdd_roc_req_q));
qdf_spin_unlock(&pHddCtx->hdd_roc_req_q_lock);
if ((isBusy == false) && (!size)) {
status = wlan_hdd_execute_remain_on_channel(pAdapter,
pRemainChanCtx);
if (status == -EBUSY) {
if (wlan_hdd_roc_request_enqueue(pAdapter,
pRemainChanCtx)) {
qdf_mem_free(pRemainChanCtx);
return -EAGAIN;
}
}
return 0;
}
if (wlan_hdd_roc_request_enqueue(pAdapter, pRemainChanCtx)) {
qdf_mem_free(pRemainChanCtx);
return -EAGAIN;
}
/*
* If a connection is not in progress (isBusy), before scheduling
* the work queue it is necessary to check if a roc in in progress
* or not because: if an roc is in progress, the dequeued roc
* that will be processed will be dropped. To ensure that this new
* roc request is not dropped, it is suggested to check if an roc
* is in progress or not. The existing roc completion will provide
* the trigger to dequeue the next roc request.
*/
if (isBusy == false && pAdapter->is_roc_inprogress == false) {
hdd_debug("scheduling delayed work: no connection/roc active");
schedule_delayed_work(&pHddCtx->roc_req_work, 0);
}
return 0;
}
#endif
#ifdef CONVERGED_P2P_ENABLE
static int __wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration,
u64 *cookie)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx;
QDF_STATUS status;
int ret;
ENTER();
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
status = wlan_cfg80211_roc(pAdapter->hdd_vdev, chan,
duration, cookie);
hdd_info("remain on channel request, status:%d", status);
return qdf_status_to_os_return(status);
}
#else
static int __wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration,
u64 *cookie)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *hdd_ctx;
int ret;
ENTER();
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (0 != ret)
return ret;
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
MTRACE(qdf_trace(QDF_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,
duration, cookie,
REMAIN_ON_CHANNEL_REQUEST);
EXIT();
return ret;
}
#endif
int wlan_hdd_cfg80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
unsigned int duration, u64 *cookie)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_remain_on_channel(wiphy,
wdev,
chan,
duration, cookie);
cds_ssr_unprotect(__func__);
return ret;
}
void hdd_remain_chan_ready_handler(hdd_adapter_t *pAdapter,
uint32_t scan_id)
{
hdd_cfg80211_state_t *cfgState = NULL;
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
QDF_STATUS status;
if (NULL == pAdapter) {
hdd_err("pAdapter is NULL");
return;
}
cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
hdd_debug("Ready on chan ind %d", scan_id);
pAdapter->start_roc_ts = (uint64_t)qdf_mc_timer_get_system_time();
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx != NULL) {
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_REMAINCHANREADYHANDLER,
pAdapter->sessionId,
pRemainChanCtx->duration));
/* start timer for actual duration */
if (QDF_TIMER_STATE_RUNNING ==
qdf_mc_timer_get_current_state(
&pRemainChanCtx->hdd_remain_on_chan_timer)) {
hdd_err("Timer Started before ready event!!!");
if (qdf_mc_timer_stop(&pRemainChanCtx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
}
status =
qdf_mc_timer_start(&pRemainChanCtx->
hdd_remain_on_chan_timer,
(pRemainChanCtx->duration +
COMPLETE_EVENT_PROPOGATE_TIME));
if (status != QDF_STATUS_SUCCESS)
hdd_err("Remain on Channel timer start failed");
if (REMAIN_ON_CHANNEL_REQUEST ==
pRemainChanCtx->rem_on_chan_request) {
cfg80211_ready_on_channel(
pAdapter->dev->
ieee80211_ptr,
(uintptr_t)
pRemainChanCtx,
&pRemainChanCtx->chan,
pRemainChanCtx->
duration, GFP_KERNEL);
} else if (OFF_CHANNEL_ACTION_TX ==
pRemainChanCtx->rem_on_chan_request) {
complete(&pAdapter->offchannel_tx_event);
}
/* Check for cached action frame */
if (pRemainChanCtx->action_pkt_buff.frame_length != 0) {
hdd_debug("Sent cached action frame to supplicant");
#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);
#else
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);
#endif /* LINUX_VERSION_CODE */
qdf_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;
}
complete(&pAdapter->rem_on_chan_ready_event);
} else {
hdd_debug("No Pending Remain on channel Request");
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
#ifdef CONVERGED_P2P_ENABLE
static int
__wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
QDF_STATUS status;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
status = wlan_cfg80211_cancel_roc(pAdapter->hdd_vdev, cookie);
hdd_info("cancel remain on channel, status:%d", status);
return 0;
}
#else
static int
__wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
struct net_device *dev = wdev->netdev;
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;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
int status;
int qdf_status;
unsigned long rc;
qdf_list_node_t *tmp, *q;
hdd_roc_req_t *curr_roc_req;
uint32_t roc_scan_id;
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return status;
qdf_spin_lock(&pHddCtx->hdd_roc_req_q_lock);
list_for_each_safe(tmp, q, &pHddCtx->hdd_roc_req_q.anchor) {
curr_roc_req = list_entry(tmp, hdd_roc_req_t, node);
if ((uintptr_t) curr_roc_req->pRemainChanCtx == cookie) {
qdf_status = qdf_list_remove_node(&pHddCtx->hdd_roc_req_q,
(qdf_list_node_t *)
curr_roc_req);
qdf_spin_unlock(&pHddCtx->hdd_roc_req_q_lock);
if (qdf_status == QDF_STATUS_SUCCESS) {
qdf_mem_free(curr_roc_req->pRemainChanCtx);
qdf_mem_free(curr_roc_req);
}
return 0;
}
}
qdf_spin_unlock(&pHddCtx->hdd_roc_req_q_lock);
/* FIXME cancel currently running remain on chan.
* Need to check cookie and cancel accordingly
*/
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx) {
hdd_debug("action_cookie = %08llx, roc cookie = %08llx, cookie = %08llx",
cfgState->action_cookie, pRemainChanCtx->cookie,
cookie);
if (pRemainChanCtx->cookie == cookie) {
/* request to cancel on-going roc */
if (cfgState->buf) {
/* Tx frame pending */
if (cfgState->action_cookie != cookie) {
hdd_debug("Cookie matched with RoC cookie but not with tx cookie, indicate expired event for roc");
/* RoC was extended to accomodate the tx frame */
if (REMAIN_ON_CHANNEL_REQUEST ==
pRemainChanCtx->
rem_on_chan_request) {
cfg80211_remain_on_channel_expired(
pRemainChanCtx->dev->
ieee80211_ptr,
pRemainChanCtx->cookie,
&pRemainChanCtx->chan,
GFP_KERNEL);
}
pRemainChanCtx->rem_on_chan_request =
OFF_CHANNEL_ACTION_TX;
pRemainChanCtx->cookie =
cfgState->action_cookie;
mutex_unlock(&cfgState->
remain_on_chan_ctx_lock);
return 0;
}
}
} else if (cfgState->buf && cfgState->action_cookie ==
cookie) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("Cookie not matched with RoC cookie but matched with tx cookie, cleanup action frame");
/*free the buf and return 0*/
hdd_cleanup_actionframe(pHddCtx, pAdapter);
return 0;
} else {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("No matching cookie");
return -EINVAL;
}
} else {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("RoC context is NULL, return success");
return 0;
}
if (NULL != cfgState->remain_on_chan_ctx) {
if (qdf_mc_timer_stop(&cfgState->remain_on_chan_ctx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
if (pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("ROC timer cancellation in progress, wait for completion");
rc = wait_for_completion_timeout(&pAdapter->
cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc)
hdd_err("wait on cancel_rem_on_chan_var timed out");
return 0;
}
pRemainChanCtx->hdd_remain_on_chan_cancel_in_progress = true;
}
roc_scan_id = pRemainChanCtx->scan_id;
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
/* wait until remain on channel ready event received
* for already issued remain on channel request
*/
rc = wait_for_completion_timeout(&pAdapter->rem_on_chan_ready_event,
msecs_to_jiffies(WAIT_REM_CHAN_READY));
if (!rc) {
hdd_err("timeout waiting for remain on channel ready indication");
if (cds_is_driver_recovering()) {
hdd_err("Recovery in Progress. State: 0x%x Ignore!!!",
cds_get_driver_state());
return -EAGAIN;
}
cds_flush_logs(WLAN_LOG_TYPE_FATAL,
WLAN_LOG_INDICATOR_HOST_DRIVER,
WLAN_LOG_REASON_HDD_TIME_OUT,
true, false);
}
INIT_COMPLETION(pAdapter->cancel_rem_on_chan_var);
/* 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 ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)
) {
uint8_t sessionId = pAdapter->sessionId;
sme_cancel_remain_on_channel(WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId, roc_scan_id);
} else if ((QDF_SAP_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == pAdapter->device_mode)
) {
wlansap_cancel_remain_on_channel(
WLAN_HDD_GET_SAP_CTX_PTR(pAdapter), roc_scan_id);
} else {
hdd_err("Invalid device_mode %s(%d)",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
return -EIO;
}
rc = wait_for_completion_timeout(&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc)
hdd_err("wait on cancel_rem_on_chan_var timed out");
hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_ROC);
EXIT();
return 0;
}
#endif
int wlan_hdd_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy,
wdev,
cookie);
cds_ssr_unprotect(__func__);
return ret;
}
#ifdef CONVERGED_P2P_ENABLE
static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
QDF_STATUS status;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
status = wlan_cfg80211_mgmt_tx(pAdapter->hdd_vdev, chan,
offchan, wait, buf, len, no_cck,
dont_wait_for_ack, cookie);
hdd_info("mgmt tx, status:%d", status);
return 0;
}
#else
static int __wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
{
struct net_device *dev = wdev->netdev;
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;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
uint16_t extendedWait = 0;
uint8_t type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]);
uint8_t subType = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]);
enum action_frm_type actionFrmType;
bool noack = 0;
int status;
unsigned long rc;
hdd_adapter_t *goAdapter;
uint16_t current_freq;
uint8_t home_ch = 0;
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_ACTION, pAdapter->sessionId,
pAdapter->device_mode));
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return status;
hdd_debug("Device_mode %s(%d) type: %d, wait: %d, offchan: %d, category: %d, actionId: %d",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode, type, wait, offchan,
buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET +
WLAN_HDD_PUBLIC_ACTION_FRAME_CATEGORY_OFFSET],
buf[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET +
WLAN_HDD_PUBLIC_ACTION_FRAME_ACTION_OFFSET]);
#ifdef WLAN_FEATURE_P2P_DEBUG
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])) {
actionFrmType = buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
if (actionFrmType >= MAX_P2P_ACTION_FRAME_TYPE) {
hdd_debug("[P2P] unknown[%d] ---> OTA to " MAC_ADDRESS_STR,
actionFrmType,
MAC_ADDR_ARRAY(&buf
[WLAN_HDD_80211_FRM_DA_OFFSET]));
} else {
hdd_debug("[P2P] %s ---> OTA to "
MAC_ADDRESS_STR,
p2p_action_frame_type[actionFrmType],
MAC_ADDR_ARRAY(&buf
[WLAN_HDD_80211_FRM_DA_OFFSET]));
if ((actionFrmType == WLAN_HDD_PROV_DIS_REQ)
&& (global_p2p_connection_status == P2P_NOT_ACTIVE)) {
global_p2p_connection_status = P2P_GO_NEG_PROCESS;
hdd_debug("[P2P State]Inactive state to GO negotiation progress state");
} else if ((actionFrmType == WLAN_HDD_GO_NEG_CNF) &&
(global_p2p_connection_status ==
P2P_GO_NEG_PROCESS)) {
global_p2p_connection_status =
P2P_GO_NEG_COMPLETED;
hdd_debug("[P2P State]GO nego progress to GO nego completed state");
}
}
}
#endif
noack = dont_wait_for_ack;
/* 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(&cfgState->remain_on_chan_ctx_lock);
if (cfgState->remain_on_chan_ctx) {
uint64_t current_time =
(uint64_t)qdf_mc_timer_get_system_time();
int remaining_roc_time =
((int) cfgState->remain_on_chan_ctx->duration -
(current_time - pAdapter->start_roc_ts));
if (remaining_roc_time > ACTION_FRAME_DEFAULT_WAIT)
wait = remaining_roc_time;
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
if ((QDF_STA_MODE == pAdapter->device_mode) &&
(type == SIR_MAC_MGMT_FRAME &&
subType == SIR_MAC_MGMT_PROBE_RSP)) {
/*
* Drop Probe response received
* from supplicant in sta mode
*/
goto err_rem_channel;
}
/*
* 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 ((QDF_SAP_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == 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.
*/
uint8_t dstMac[ETH_ALEN] = { 0 };
memcpy(&dstMac,
&buf[WLAN_HDD_80211_FRM_DA_OFFSET],
ETH_ALEN);
hdd_debug("Deauth/Disassoc received for STA:"
MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(dstMac));
goto err_rem_channel;
}
}
}
if (NULL != cfgState->buf) {
if (!noack) {
hdd_err("Previous P2P Action frame packet pending");
hdd_cleanup_actionframe(pAdapter->pHddCtx, pAdapter);
} else {
hdd_err("Pending Action frame packet return EBUSY");
return -EBUSY;
}
}
if (subType == SIR_MAC_MGMT_ACTION) {
hdd_debug("Action frame tx request : %s",
hdd_get_action_string(buf
[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET]));
}
if ((pAdapter->device_mode == QDF_SAP_MODE) &&
(test_bit(SOFTAP_BSS_STARTED, &pAdapter->event_flags))) {
home_ch = pAdapter->sessionCtx.ap.operatingChannel;
} else if ((pAdapter->device_mode == QDF_STA_MODE) &&
(pAdapter->sessionCtx.station.conn_info.connState ==
eConnectionState_Associated)) {
home_ch =
pAdapter->sessionCtx.station.conn_info.operationChannel;
} else {
goAdapter = hdd_get_adapter(pAdapter->pHddCtx, QDF_P2P_GO_MODE);
if (goAdapter &&
(test_bit(SOFTAP_BSS_STARTED, &goAdapter->event_flags)))
home_ch = goAdapter->sessionCtx.ap.operatingChannel;
}
if (chan &&
(ieee80211_frequency_to_channel(chan->center_freq) ==
home_ch)) {
/* if adapter is already on requested ch, no need for ROC */
wait = 0;
hdd_debug("Adapter already on requested ch. No ROC needed");
goto send_frame;
}
if (offchan && wait && chan) {
int status;
enum rem_on_channel_request_type 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(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType == SIR_MAC_MGMT_ACTION) &&
hdd_p2p_is_action_type_rsp(&buf
[WLAN_HDD_PUBLIC_ACTION_FRAME_BODY_OFFSET])
&& cfgState->remain_on_chan_ctx
&& cfgState->current_freq == chan->center_freq) {
if (QDF_TIMER_STATE_RUNNING ==
qdf_mc_timer_get_current_state(&cfgState->
remain_on_chan_ctx->
hdd_remain_on_chan_timer)) {
/* In the latest wpa_supplicant, the wait time
* for go negotiation response is set to 100ms,
* due to which, there could be a possibility
* that, if the go negotaition confirmation
* frame is not received within 100 msec, ROC
* would be timeout and resulting in connection
* failures as the device will not be on the
* listen channel anymore to receive the conf
* frame. Also wpa_supplicant has set the wait
* to 50msec for go negotiation confirmation,
* invitation response and prov discovery rsp
* frames. So increase the wait time for all
* these frames.
*/
actionFrmType = buf
[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
if (actionFrmType == WLAN_HDD_GO_NEG_RESP ||
actionFrmType == WLAN_HDD_PROV_DIS_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;
hdd_debug("Extending the wait time %d for actionFrmType=%d",
wait, actionFrmType);
if (qdf_mc_timer_stop(&cfgState->
remain_on_chan_ctx->
hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_err("Failed to stop hdd_remain_on_chan_timer");
status =
qdf_mc_timer_start(&cfgState->
remain_on_chan_ctx->
hdd_remain_on_chan_timer,
wait);
if (status != QDF_STATUS_SUCCESS)
hdd_warn("Remain on Channel timer start failed");
mutex_unlock(&cfgState->
remain_on_chan_ctx_lock);
goto send_frame;
} else {
if (pRemainChanCtx->
hdd_remain_on_chan_cancel_in_progress ==
true) {
mutex_unlock(&cfgState->
remain_on_chan_ctx_lock);
hdd_debug("action frame tx: waiting for completion of ROC ");
rc = wait_for_completion_timeout
(&pAdapter->cancel_rem_on_chan_var,
msecs_to_jiffies
(WAIT_CANCEL_REM_CHAN));
if (!rc)
hdd_warn("wait on cancel_rem_on_chan_var timed out");
} else
mutex_unlock(&cfgState->
remain_on_chan_ctx_lock);
}
} else
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
/* At this point if remain_on_chan_ctx exists but timer not
* running means that roc workqueue requested a new RoC and it
* is in progress. So wait for Ready on channel indication
*/
if ((pRemainChanCtx) &&
(QDF_TIMER_STATE_RUNNING !=
qdf_mc_timer_get_current_state(
&pRemainChanCtx->hdd_remain_on_chan_timer))) {
hdd_debug("remain_on_chan_ctx exists but RoC timer not running. wait for ready on channel");
rc = wait_for_completion_timeout(&pAdapter->
rem_on_chan_ready_event,
msecs_to_jiffies
(WAIT_REM_CHAN_READY));
if (!rc)
hdd_err("timeout waiting for remain on channel ready indication");
}
if ((pRemainChanCtx != NULL) &&
(cfgState->current_freq == chan->center_freq)) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
hdd_debug("action frame: extending the wait time");
extendedWait = (uint16_t) wait;
goto send_frame;
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
INIT_COMPLETION(pAdapter->offchannel_tx_event);
status = wlan_hdd_request_remain_on_channel(wiphy, dev, chan,
wait, cookie,
req_type);
if (0 != status) {
if ((-EBUSY == status) &&
(cfgState->current_freq == chan->center_freq)) {
goto send_frame;
}
goto err_rem_channel;
}
/* This will extend timer in LIM when sending Any action frame
* It will cover remain on channel timer till next action frame
* in rx direction.
*/
extendedWait = (uint16_t) wait;
/* Wait for driver to be ready on the requested channel */
rc = wait_for_completion_timeout(&pAdapter->offchannel_tx_event,
msecs_to_jiffies
(WAIT_CHANGE_CHANNEL_FOR_OFFCHANNEL_TX));
if (!rc) {
hdd_err("wait on offchannel_tx_event timed out");
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:
if (!noack) {
cfgState->buf = qdf_mem_malloc(len); /* buf; */
if (cfgState->buf == NULL)
return -ENOMEM;
cfgState->len = len;
qdf_mem_copy(cfgState->buf, buf, len);
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
if (cfgState->remain_on_chan_ctx) {
cfgState->action_cookie =
cfgState->remain_on_chan_ctx->cookie;
*cookie = cfgState->action_cookie;
} else {
*cookie = (uintptr_t) cfgState->buf;
cfgState->action_cookie = *cookie;
}
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
/*
* Firmware needs channel information for action frames
* which are not sent on the current operating channel of VDEV
*/
if ((QDF_P2P_DEVICE_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_GO_MODE == pAdapter->device_mode)) {
if (chan && (chan->center_freq != 0))
current_freq = chan->center_freq;
else
current_freq = cfgState->current_freq;
} else {
current_freq = 0;
}
INIT_COMPLETION(pAdapter->tx_action_cnf_event);
if ((QDF_STA_MODE == pAdapter->device_mode) ||
(QDF_P2P_CLIENT_MODE == pAdapter->device_mode) ||
(QDF_P2P_DEVICE_MODE == pAdapter->device_mode)) {
uint8_t 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])) {
actionFrmType =
buf[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
hdd_debug("Tx Action Frame %u", actionFrmType);
if (actionFrmType == WLAN_HDD_PROV_DIS_REQ) {
cfgState->actionFrmState =
HDD_PD_REQ_ACK_PENDING;
hdd_debug("HDD_PD_REQ_ACK_PENDING");
} else if (actionFrmType == WLAN_HDD_GO_NEG_REQ) {
cfgState->actionFrmState =
HDD_GO_NEG_REQ_ACK_PENDING;
hdd_debug("HDD_GO_NEG_REQ_ACK_PENDING");
}
}
if (QDF_STATUS_SUCCESS !=
sme_send_action(WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId, buf, len, extendedWait, noack,
current_freq)) {
hdd_err("sme_send_action returned fail");
goto err;
}
} else if (QDF_SAP_MODE == pAdapter->device_mode ||
QDF_P2P_GO_MODE == pAdapter->device_mode) {
if (QDF_STATUS_SUCCESS !=
wlansap_send_action(WLAN_HDD_GET_SAP_CTX_PTR(pAdapter),
buf, len, 0, current_freq)) {
hdd_err("wlansap_send_action returned fail");
goto err;
}
}
return 0;
err:
if (!noack) {
hdd_send_action_cnf(pAdapter, false);
}
return 0;
err_rem_channel:
*cookie = (uintptr_t) cfgState;
cfg80211_mgmt_tx_status(
pAdapter->dev->ieee80211_ptr,
*cookie, buf, len, false, GFP_KERNEL);
EXIT();
return 0;
}
#endif
#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)
#else
int wlan_hdd_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait,
const u8 *buf, size_t len, bool no_cck,
bool dont_wait_for_ack, u64 *cookie)
#endif /* LINUX_VERSION_CODE */
{
int ret;
cds_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);
#else
ret = __wlan_hdd_mgmt_tx(wiphy, wdev, chan, offchan,
wait, buf, len, no_cck,
dont_wait_for_ack, cookie);
#endif /* LINUX_VERSION_CODE */
cds_ssr_unprotect(__func__);
return ret;
}
#ifdef CONVERGED_P2P_ENABLE
static int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
QDF_STATUS status;
struct net_device *dev = wdev->netdev;
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
if (wlan_hdd_validate_session_id(pAdapter->sessionId)) {
hdd_err("invalid session id: %d", pAdapter->sessionId);
return -EINVAL;
}
status = wlan_cfg80211_mgmt_tx_cancel(pAdapter->hdd_vdev,
cookie);
hdd_info("cancel mgmt tx, status:%d", status);
return 0;
}
#else
static int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
return wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, wdev, cookie);
}
#endif
int wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(wiphy, wdev, cookie);
cds_ssr_unprotect(__func__);
return ret;
}
void hdd_send_action_cnf(hdd_adapter_t *pAdapter, bool 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.
*/
hdd_debug("Drop the pending ack received in cfgState->actionFrmState %d",
cfgState->actionFrmState);
return;
}
hdd_debug("Send Action cnf, actionSendSuccess %d",
actionSendSuccess);
/*
* 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(
pAdapter->dev->ieee80211_ptr,
cfgState->action_cookie,
cfgState->buf, cfgState->len,
actionSendSuccess, GFP_KERNEL);
qdf_mem_free(cfgState->buf);
cfgState->buf = NULL;
complete(&pAdapter->tx_action_cnf_event);
}
/**
* hdd_send_action_cnf_cb - action confirmation callback
* @session_id: SME session ID
* @tx_completed: ack status
*
* This function invokes hdd_sendActionCnf to update ack status to
* supplicant.
*/
void hdd_send_action_cnf_cb(uint32_t session_id, bool tx_completed)
{
hdd_context_t *hdd_ctx;
hdd_adapter_t *adapter;
ENTER();
/* Get the HDD context.*/
hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
if (0 != wlan_hdd_validate_context(hdd_ctx))
return;
adapter = hdd_get_adapter_by_sme_session_id(hdd_ctx, session_id);
if (NULL == adapter) {
hdd_err("adapter not found");
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hdd_err("adapter has invalid magic");
return;
}
hdd_send_action_cnf(adapter, tx_completed);
}
/**
* hdd_set_p2p_noa
*
***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_set_p2p_noa(struct net_device *dev, uint8_t *command)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tP2pPsConfig NoA;
int count, duration, interval;
char *param;
int ret;
param = strnchr(command, strlen(command), ' ');
if (param == NULL) {
hdd_err("strnchr failed to find delimeter");
return -EINVAL;
}
param++;
ret = sscanf(param, "%d %d %d", &count, &interval, &duration);
if (ret != 3) {
hdd_err("P2P_SET GO NoA: fail to read params, ret=%d",
ret);
return -EINVAL;
}
if (count < 0 || interval < 0 || duration < 0 ||
interval > MAX_MUS_VAL || duration > MAX_MUS_VAL) {
hdd_err("Invalid NOA parameters");
return -EINVAL;
}
hdd_debug("P2P_SET GO NoA: count=%d interval=%d duration=%d",
count, interval, duration);
duration = MS_TO_TU_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_TU_MUS(interval);
NoA.count = count;
NoA.sessionid = pAdapter->sessionId;
hdd_debug("P2P_PS_ATTR:oppPS %d ctWindow %d duration %d "
"interval %d count %d single noa duration %d "
"PsSelection %x", NoA.opp_ps,
NoA.ctWindow, NoA.duration, NoA.interval,
NoA.count, NoA.single_noa_duration, NoA.psSelection);
return wlan_hdd_set_power_save(pAdapter, &NoA);
}
/**
* hdd_set_p2p_opps
*
***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_set_p2p_opps(struct net_device *dev, uint8_t *command)
{
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
tP2pPsConfig noa;
char *param;
int legacy_ps, opp_ps, ctwindow;
int ret;
param = strnchr(command, strlen(command), ' ');
if (param == NULL) {
hdd_err("strnchr failed to find delimiter");
return -EINVAL;
}
param++;
ret = sscanf(param, "%d %d %d", &legacy_ps, &opp_ps, &ctwindow);
if (ret != 3) {
hdd_err("P2P_SET GO PS: fail to read params, ret=%d", ret);
return -EINVAL;
}
if ((opp_ps != -1) && (opp_ps != 0) && (opp_ps != 1)) {
hdd_err("Invalid opp_ps value:%d", opp_ps);
return -EINVAL;
}
/* P2P spec: 3.3.2 Power Management and discovery:
* CTWindow should be at least 10 TU.
* P2P spec: Table 27 - CTWindow and OppPS Parameters field format:
* CTWindow and OppPS Parameters together is 8 bits.
* CTWindow uses 7 bits (0-6, Bit 7 is for OppPS)
* 0 indicates that there shall be no CTWindow
*/
if ((ctwindow != -1) && (ctwindow != 0) &&
(!((ctwindow >= 10) && (ctwindow <= 127)))) {
hdd_err("Invalid CT window value:%d", ctwindow);
return -EINVAL;
}
hdd_debug("P2P_SET GO PS: legacy_ps=%d opp_ps=%d ctwindow=%d",
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)
*
* e.g., 1: P2P_SET_PS 1 1 30
* Driver sets the Opps and CTwindow as 30 and send it to FW.
* e.g., 2: P2P_SET_PS 1 -1 15
* Driver caches the CTwindow value but not send the command to FW.
* e.g., 3: P2P_SET_PS 1 1 -1
* Driver sends the command to FW with Opps enabled and CT window as
* 15 (last cached CTWindow value).
* (or) : P2P_SET_PS 1 1 20
* Driver sends the command to FW with opps enabled and CT window
* as 20.
*
* legacy_ps param remains unused until required in the future.
*/
if (ctwindow != -1)
adapter->ctw = ctwindow;
/* Send command to FW when OppPS is either enabled(1)/disbaled(0) */
if (opp_ps != -1) {
adapter->ops = opp_ps;
noa.opp_ps = adapter->ops;
noa.ctWindow = adapter->ctw;
noa.duration = 0;
noa.single_noa_duration = 0;
noa.interval = 0;
noa.count = 0;
noa.psSelection = P2P_POWER_SAVE_TYPE_OPPORTUNISTIC;
noa.sessionid = adapter->sessionId;
hdd_debug("P2P_PS_ATTR: oppPS %d ctWindow %d duration %d interval %d count %d single noa duration %d PsSelection %x",
noa.opp_ps, noa.ctWindow,
noa.duration, noa.interval, noa.count,
noa.single_noa_duration,
noa.psSelection);
wlan_hdd_set_power_save(adapter, &noa);
}
return 0;
}
int hdd_set_p2p_ps(struct net_device *dev, void *msgData)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tP2pPsConfig NoA;
struct p2p_app_set_ps *pappNoA = (struct p2p_app_set_ps *) 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;
return wlan_hdd_set_power_save(pAdapter, &NoA);
}
static uint8_t wlan_hdd_get_session_type(enum nl80211_iftype type)
{
switch (type) {
case NL80211_IFTYPE_AP:
return QDF_SAP_MODE;
case NL80211_IFTYPE_P2P_GO:
return QDF_P2P_GO_MODE;
case NL80211_IFTYPE_P2P_CLIENT:
return QDF_P2P_CLIENT_MODE;
case NL80211_IFTYPE_STATION:
return QDF_STA_MODE;
default:
return QDF_STA_MODE;
}
}
/**
* __wlan_hdd_add_virtual_intf() - Add virtual interface
* @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: moniter configuraiton flags (not used)
* @vif_params: virtual interface parameters (not used)
*
* Return: the pointer of wireless dev, otherwise ERR_PTR.
*/
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)
{
hdd_context_t *pHddCtx = (hdd_context_t *) wiphy_priv(wiphy);
hdd_adapter_t *pAdapter = NULL;
hdd_scaninfo_t *scan_info = NULL;
int ret;
uint8_t session_type;
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return ERR_PTR(-EINVAL);
}
ret = wlan_hdd_validate_context(pHddCtx);
if (0 != ret)
return ERR_PTR(ret);
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_ADD_VIRTUAL_INTF, NO_SESSION, type));
/*
* Allow addition multiple interfaces for QDF_P2P_GO_MODE,
* QDF_SAP_MODE, QDF_P2P_CLIENT_MODE and QDF_STA_MODE
* session type.
*/
session_type = wlan_hdd_get_session_type(type);
if (hdd_get_adapter(pHddCtx, session_type) != NULL
&& QDF_SAP_MODE != session_type
&& QDF_P2P_GO_MODE != session_type
&& QDF_P2P_CLIENT_MODE != session_type
&& QDF_STA_MODE != session_type) {
hdd_err("Interface type %d already exists. Two interfaces of same type are not supported currently.",
type);
return ERR_PTR(-EINVAL);
}
pAdapter = hdd_get_adapter(pHddCtx, QDF_STA_MODE);
if ((pAdapter != NULL) &&
!(wlan_hdd_validate_session_id(pAdapter->sessionId))) {
scan_info = &pAdapter->scan_info;
if (scan_info->mScanPending) {
wlan_abort_scan(pHddCtx->hdd_pdev, INVAL_PDEV_ID,
pAdapter->sessionId, INVALID_SCAN_ID, false);
hdd_debug("Abort Scan while adding virtual interface");
}
}
pAdapter = NULL;
if (pHddCtx->config->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.
*/
struct qdf_mac_addr p2pDeviceAddress =
pHddCtx->p2pDeviceAddress;
p2pDeviceAddress.bytes[4] ^= 0x80;
pAdapter = hdd_open_adapter(pHddCtx,
session_type,
name, p2pDeviceAddress.bytes,
name_assign_type,
true);
} else {
pAdapter = hdd_open_adapter(pHddCtx,
session_type,
name,
wlan_hdd_get_intf_addr(pHddCtx),
name_assign_type,
true);
}
if (NULL == pAdapter) {
hdd_err("hdd_open_adapter failed");
return ERR_PTR(-ENOSPC);
}
/*
* Add interface can be requested from the upper layer at any time
* check the statemachine for modules state and if they are closed
* open the modules.
*/
ret = hdd_wlan_start_modules(pHddCtx, pAdapter, false);
if (ret) {
hdd_err("Failed to start the wlan_modules");
goto close_adapter;
}
/*
* Once the support for session creation/deletion from
* hdd_hostapd_open/hdd_host_stop is in place.
* The support for starting adapter from here can be removed.
*/
if (NL80211_IFTYPE_AP == type || (NL80211_IFTYPE_P2P_GO == type)) {
ret = hdd_start_adapter(pAdapter);
if (ret) {
hdd_err("Failed to start %s", name);
goto stop_modules;
}
}
if (pHddCtx->rps)
hdd_send_rps_ind(pAdapter);
EXIT();
return pAdapter->dev->ieee80211_ptr;
stop_modules:
/*
* Find if any iface is up. If there is not iface which is up
* start the timer to close the modules
*/
if (hdd_check_for_opened_interfaces(pHddCtx)) {
hdd_debug("Closing all modules from the add_virt_iface");
qdf_mc_timer_start(&pHddCtx->iface_change_timer,
pHddCtx->config->iface_change_wait_time);
} else
hdd_debug("Other interfaces are still up dont close modules!");
close_adapter:
hdd_close_adapter(pHddCtx, pAdapter, true);
return ERR_PTR(-EINVAL);
}
#if (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;
cds_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, flags, params);
cds_ssr_unprotect(__func__);
return wdev;
}
#else
/**
* 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;
cds_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, flags, params);
cds_ssr_unprotect(__func__);
return wdev;
}
#endif
int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct net_device *dev = wdev->netdev;
hdd_context_t *pHddCtx = (hdd_context_t *) wiphy_priv(wiphy);
hdd_adapter_t *pVirtAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
int status;
ENTER();
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
hdd_err("Command not allowed in FTM mode");
return -EINVAL;
}
MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
TRACE_CODE_HDD_DEL_VIRTUAL_INTF,
pVirtAdapter->sessionId, pVirtAdapter->device_mode));
hdd_debug("Device_mode %s(%d)",
hdd_device_mode_to_string(pVirtAdapter->device_mode),
pVirtAdapter->device_mode);
status = wlan_hdd_validate_context(pHddCtx);
if (0 != status)
return status;
/* check state machine state and kickstart modules if they are closed */
status = hdd_wlan_start_modules(pHddCtx, pVirtAdapter, false);
if (status)
return status;
if (pVirtAdapter->device_mode == QDF_SAP_MODE &&
wlan_sap_is_pre_cac_active(pHddCtx->hHal)) {
hdd_clean_up_pre_cac_interface(pHddCtx);
} else {
wlan_hdd_release_intf_addr(pHddCtx,
pVirtAdapter->macAddressCurrent.bytes);
hdd_stop_adapter(pHddCtx, pVirtAdapter, true);
hdd_close_adapter(pHddCtx, pVirtAdapter, true);
}
EXIT();
return 0;
}
int wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_del_virtual_intf(wiphy, wdev);
cds_ssr_unprotect(__func__);
return ret;
}
#ifdef WLAN_FEATURE_P2P_DEBUG
/*
* wlan_hdd_p2p_action_debug() - Log P2P state and update global status
* @actionFrmType: action frame type
* @macFrom: peer mac address
*
* return: void
*/
static void wlan_hdd_p2p_action_debug(enum action_frm_type actionFrmType,
uint8_t *macFrom)
{
if (actionFrmType >= MAX_P2P_ACTION_FRAME_TYPE) {
hdd_debug("[P2P] unknown[%d] <--- OTA from " MAC_ADDRESS_STR,
actionFrmType, MAC_ADDR_ARRAY(macFrom));
} else {
hdd_debug("[P2P] %s <--- OTA from " MAC_ADDRESS_STR,
p2p_action_frame_type[actionFrmType],
MAC_ADDR_ARRAY(macFrom));
if ((actionFrmType == WLAN_HDD_PROV_DIS_REQ)
&& (global_p2p_connection_status == P2P_NOT_ACTIVE)) {
global_p2p_connection_status = P2P_GO_NEG_PROCESS;
hdd_debug("[P2P State]Inactive state to GO negotiation progress state");
} else
if ((actionFrmType == WLAN_HDD_GO_NEG_CNF)
&& (global_p2p_connection_status == P2P_GO_NEG_PROCESS)) {
global_p2p_connection_status = P2P_GO_NEG_COMPLETED;
hdd_debug("[P2P State]GO negotiation progress to GO negotiation completed state");
} else
if ((actionFrmType == WLAN_HDD_INVITATION_REQ)
&& (global_p2p_connection_status == P2P_NOT_ACTIVE)) {
global_p2p_connection_status = P2P_GO_NEG_COMPLETED;
hdd_debug("[P2P State]Inactive state to GO negotiation completed state Autonomous GO formation");
}
}
}
#else
/*
* wlan_hdd_p2p_action_debug() - dummy
* @actionFrmType: action frame type
* @macFrom: peer mac address
*
* return: void
*/
static void wlan_hdd_p2p_action_debug(enum action_frm_type actionFrmType,
uint8_t *macFrom)
{
}
#endif
void __hdd_indicate_mgmt_frame(hdd_adapter_t *pAdapter,
uint32_t nFrameLength,
uint8_t *pbFrames,
uint8_t frameType, uint32_t rxChan, int8_t rxRssi)
{
uint16_t freq;
uint16_t extend_time;
uint8_t type = 0;
uint8_t subType = 0;
enum action_frm_type actionFrmType;
hdd_cfg80211_state_t *cfgState = NULL;
QDF_STATUS status;
hdd_remain_on_chan_ctx_t *pRemainChanCtx = NULL;
hdd_context_t *pHddCtx;
uint8_t broadcast = 0;
hdd_debug("Frame Type = %d Frame Length = %d",
frameType, nFrameLength);
if (NULL == pAdapter) {
hdd_err("pAdapter is NULL");
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 == nFrameLength) {
hdd_err("Frame Length is Invalid ZERO");
return;
}
if (NULL == pbFrames) {
hdd_err("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) &&
!qdf_is_macaddr_broadcast(
(struct qdf_mac_addr *)&pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET])) {
pAdapter =
hdd_get_adapter_by_macaddr(pHddCtx,
&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 are dropping action frame
*/
hdd_err("pAdapter for action frame is NULL Macaddr = "
MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(&pbFrames
[WLAN_HDD_80211_FRM_DA_OFFSET]));
hdd_debug("Frame Type = %d Frame Length = %d subType = %d",
frameType, nFrameLength, subType);
/*
* We will receive broadcast management frames
* in OCB mode
*/
pAdapter = hdd_get_adapter(pHddCtx, QDF_OCB_MODE);
if (NULL == pAdapter || !qdf_is_macaddr_broadcast(
(struct qdf_mac_addr *)&pbFrames
[WLAN_HDD_80211_FRM_DA_OFFSET])) {
/*
* Under assumtion that we don't
* receive any action frame with BCST
* as destination, we are dropping
* action frame
*/
return;
}
broadcast = 1;
}
}
if (NULL == pAdapter->dev) {
hdd_err("pAdapter->dev is NULL");
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) {
hdd_err("pAdapter has invalid magic");
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,
NL80211_BAND_2GHZ);
else
freq = ieee80211_channel_to_frequency(rxChan,
NL80211_BAND_5GHZ);
cfgState = WLAN_HDD_GET_CFG_STATE_PTR(pAdapter);
if ((type == SIR_MAC_MGMT_FRAME) &&
(subType == SIR_MAC_MGMT_ACTION) && !broadcast) {
if (pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] ==
WLAN_HDD_PUBLIC_ACTION_FRAME) {
/* Public action frame */
if ((pbFrames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1]
== SIR_MAC_ACTION_VENDOR_SPECIFIC) &&
!qdf_mem_cmp(&pbFrames
[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET
+ 2], SIR_MAC_P2P_OUI,
SIR_MAC_P2P_OUI_SIZE)) {
/* P2P action frames */
uint8_t *macFrom = &pbFrames
[WLAN_HDD_80211_PEER_ADDR_OFFSET];
actionFrmType =
pbFrames
[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
hdd_debug("Rx Action Frame %u",
actionFrmType);
wlan_hdd_p2p_action_debug(actionFrmType,
macFrom);
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if (pRemainChanCtx != NULL) {
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) {
hdd_debug("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 (QDF_TIMER_STATE_RUNNING == qdf_mc_timer_get_current_state(&pRemainChanCtx->hdd_remain_on_chan_timer)) {
qdf_mc_timer_stop
(&pRemainChanCtx->
hdd_remain_on_chan_timer);
status =
qdf_mc_timer_start
(&pRemainChanCtx->
hdd_remain_on_chan_timer,
extend_time);
if (status !=
QDF_STATUS_SUCCESS) {
hdd_err("Remain on Channel timer start failed");
}
} else {
hdd_debug("Rcvd action frame after timer expired");
}
} 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 =
qdf_mem_malloc
(nFrameLength);
qdf_mem_copy
(pRemainChanCtx->
action_pkt_buff.
frame_ptr,
pbFrames,
nFrameLength);
hdd_debug("Action Pkt Cached successfully !!!");
} else {
hdd_err("Frames are pending. dropping frame !!!");
}
mutex_unlock(&cfgState->
remain_on_chan_ctx_lock);
return;
}
}
}
mutex_unlock(&cfgState->
remain_on_chan_ctx_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))) {
hdd_debug("ACK_PENDING and But received RESP for Action frame ");
cfgState->is_go_neg_ack_received = 1;
hdd_send_action_cnf(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_PEER_ADDR_OFFSET];
hdd_debug("[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);
cds_tdls_tx_rx_mgmt_event(SIR_MAC_ACTION_TDLS,
SIR_MAC_ACTION_RX, SIR_MAC_MGMT_ACTION,
WLAN_HDD_PUBLIC_ACTION_TDLS_DISC_RESP,
&pbFrames[WLAN_HDD_80211_PEER_ADDR_OFFSET]);
}
#endif
}
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) {
hdd_debug("[TDLS] unknown[%d] <--- OTA",
actionFrmType);
} else {
hdd_debug("[TDLS] %s <--- OTA",
tdls_action_frame_type[actionFrmType]);
}
cds_tdls_tx_rx_mgmt_event(SIR_MAC_ACTION_TDLS,
SIR_MAC_ACTION_RX, SIR_MAC_MGMT_ACTION,
actionFrmType,
&pbFrames[WLAN_HDD_80211_PEER_ADDR_OFFSET]);
}
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_update_dsc_pto_up_mapping(pHddCtx->hHal,
pAdapter->hddWmmDscpToUpMap,
pAdapter->sessionId);
}
}
/* Indicate Frame Over Normal Interface */
hdd_debug("Indicate Frame over NL80211 sessionid : %d, idx :%d",
pAdapter->sessionId, pAdapter->dev->ifindex);
#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);
#else
cfg80211_rx_mgmt(pAdapter->dev->ieee80211_ptr, freq,
rxRssi * 100,
pbFrames, nFrameLength, GFP_ATOMIC);
#endif /* LINUX_VERSION_CODE */
}
#ifdef CONVERGED_P2P_ENABLE
int wlan_hdd_set_power_save(hdd_adapter_t *adapter,
tpP2pPsConfig pnoa)
{
struct wlan_objmgr_psoc *psoc;
hdd_context_t *hdd_ctx;
struct p2p_ps_config ps_config;
QDF_STATUS status;
if (!adapter || !pnoa) {
hdd_err("null param, adapter:%p, pnoa:%p",
adapter, pnoa);
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
psoc = hdd_ctx->hdd_psoc;
if (!psoc) {
hdd_err("psoc is null");
return -EINVAL;
}
ps_config.opp_ps = pnoa->opp_ps;
ps_config.ct_window = pnoa->ctWindow;
ps_config.duration = pnoa->duration;
ps_config.interval = pnoa->interval;
ps_config.count = pnoa->count;
ps_config.single_noa_duration = pnoa->single_noa_duration;
ps_config.ps_selection = pnoa->psSelection;
ps_config.vdev_id = pnoa->sessionid;
hdd_info("opp ps:%d, ct window:%d, duration:%d, interval:%d, count:%d, single noa duration:%d, ps selection:%d, vdev id:%d",
ps_config.opp_ps, ps_config.ct_window,
ps_config.duration, ps_config.interval,
ps_config.count, ps_config.single_noa_duration,
ps_config.ps_selection, ps_config.vdev_id);
status = ucfg_p2p_set_ps(psoc, &ps_config);
hdd_info("p2p set power save, status:%d", status);
return qdf_status_to_os_return(status);
}
#else
int wlan_hdd_set_power_save(hdd_adapter_t *adapter,
tpP2pPsConfig pnoa)
{
tHalHandle handle;
QDF_STATUS status;
if (!adapter || !pnoa) {
hdd_err("null param, adapter:%p, pnoa:%p",
adapter, pnoa);
return -EINVAL;
}
handle = WLAN_HDD_GET_HAL_CTX(adapter);
status = sme_p2p_set_ps(handle, pnoa);
hdd_info("p2p set power save, status:%d", status);
return qdf_status_to_os_return(status);
}
#endif
#ifdef CONVERGED_P2P_ENABLE
int wlan_hdd_listen_offload_start(hdd_adapter_t *adapter,
struct sir_p2p_lo_start *params)
{
struct wlan_objmgr_psoc *psoc;
struct p2p_lo_start lo_start;
hdd_context_t *hdd_ctx;
QDF_STATUS status;
if (!adapter || !params) {
hdd_err("null param, adapter:%p, params:%p",
adapter, params);
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
psoc = hdd_ctx->hdd_psoc;
if (!psoc) {
hdd_err("psoc is null");
return -EINVAL;
}
lo_start.vdev_id = params->vdev_id;
lo_start.ctl_flags = params->ctl_flags;
lo_start.freq = params->freq;
lo_start.period = params->period;
lo_start.interval = params->interval;
lo_start.count = params->count;
lo_start.device_types = params->device_types;
lo_start.dev_types_len = params->dev_types_len;
lo_start.probe_resp_tmplt = params->probe_resp_tmplt;
lo_start.probe_resp_len = params->probe_resp_len;
status = ucfg_p2p_lo_start(psoc, &lo_start);
hdd_info("p2p listen offload start, status:%d", status);
return qdf_status_to_os_return(status);
}
#else
int wlan_hdd_listen_offload_start(hdd_adapter_t *adapter,
struct sir_p2p_lo_start *params)
{
QDF_STATUS status;
if (!params) {
hdd_err("params is null, params:%p", params);
return -EINVAL;
}
status = wma_p2p_lo_start(params);
hdd_info("p2p listen offload start, status:%d", status);
return qdf_status_to_os_return(status);
}
#endif
#ifdef CONVERGED_P2P_ENABLE
int wlan_hdd_listen_offload_stop(hdd_adapter_t *adapter)
{
struct wlan_objmgr_psoc *psoc;
hdd_context_t *hdd_ctx;
uint32_t vdev_id;
QDF_STATUS status;
if (!adapter) {
hdd_err("adapter is null, adapter:%p", adapter);
return -EINVAL;
}
vdev_id = (uint32_t)adapter->sessionId;
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
psoc = hdd_ctx->hdd_psoc;
if (!psoc) {
hdd_err("psoc is null");
return -EINVAL;
}
status = ucfg_p2p_lo_stop(psoc, vdev_id);
hdd_info("p2p listen offload stop, status:%d", status);
return qdf_status_to_os_return(status);
}
#else
int wlan_hdd_listen_offload_stop(hdd_adapter_t *adapter)
{
QDF_STATUS status;
if (!adapter) {
hdd_err("adapter is null, adapter:%p", adapter);
return -EINVAL;
}
status = wma_p2p_lo_stop(adapter->sessionId);
hdd_info("p2p listen offload stop, status:%d", status);
return qdf_status_to_os_return(status);
}
#endif
/**
* wlan_hdd_update_mcc_adaptive_scheduler() - Function to update
* MAS value to FW
* @adapter: adapter object data
* @is_enable: 0-Disable, 1-Enable MAS
*
* This function passes down the value of MAS to UMAC
*
* Return: 0 for success else non zero
*
*/
static int32_t wlan_hdd_update_mcc_adaptive_scheduler(
hdd_adapter_t *adapter, bool is_enable)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (hdd_ctx == NULL) {
hdd_err("HDD context is null");
return -EINVAL;
}
hdd_info("enable/disable MAS :%d", is_enable);
if (hdd_ctx->config &&
hdd_ctx->config->enableMCCAdaptiveScheduler) {
/* Todo check where to set the MCC apative SCHED for read */
if (QDF_STATUS_SUCCESS != sme_set_mas(is_enable)) {
hdd_err("Failed to enable/disable MAS");
return -EAGAIN;
}
}
return 0;
}
/**
* wlan_hdd_update_mcc_p2p_quota() - Function to Update P2P
* quota to FW
* @adapter: Pointer to HDD adapter
* @is_set: 0-reset, 1-set
*
* This function passes down the value of MAS to UMAC
*
* Return: none
*
*/
static void wlan_hdd_update_mcc_p2p_quota(hdd_adapter_t *adapter,
bool is_set)
{
hdd_info("Set/reset P2P quota: %d", is_set);
if (is_set) {
if (adapter->device_mode == QDF_STA_MODE)
wlan_hdd_set_mcc_p2p_quota(adapter,
100 - HDD_DEFAULT_MCC_P2P_QUOTA
);
else if (adapter->device_mode == QDF_P2P_GO_MODE)
wlan_hdd_go_set_mcc_p2p_quota(adapter,
HDD_DEFAULT_MCC_P2P_QUOTA);
else
wlan_hdd_set_mcc_p2p_quota(adapter,
HDD_DEFAULT_MCC_P2P_QUOTA);
} else {
if (adapter->device_mode == QDF_P2P_GO_MODE)
wlan_hdd_go_set_mcc_p2p_quota(adapter,
HDD_RESET_MCC_P2P_QUOTA);
else
wlan_hdd_set_mcc_p2p_quota(adapter,
HDD_RESET_MCC_P2P_QUOTA);
}
}
int32_t wlan_hdd_set_mas(hdd_adapter_t *adapter, uint8_t mas_value)
{
int32_t ret = 0;
if (!adapter) {
hdd_err("Adapter is NULL");
return -EINVAL;
}
if (mas_value) {
hdd_info("Miracast is ON. Disable MAS and configure P2P quota");
ret = wlan_hdd_update_mcc_adaptive_scheduler(
adapter, false);
if (0 != ret) {
hdd_err("Failed to disable MAS");
goto done;
}
/* Config p2p quota */
wlan_hdd_update_mcc_p2p_quota(adapter, true);
} else {
hdd_info("Miracast is OFF. Enable MAS and reset P2P quota");
wlan_hdd_update_mcc_p2p_quota(adapter, false);
ret = wlan_hdd_update_mcc_adaptive_scheduler(
adapter, true);
if (0 != ret) {
hdd_err("Failed to enable MAS");
goto done;
}
}
done:
return ret;
}
/**
* set_first_connection_operating_channel() - Function to set
* first connection oerating channel
* @adapter: adapter data
* @set_value: Quota value for the interface
* @dev_mode: Device mode
* This function is used to set the first adapter operating
* channel
*
* Return: operating channel updated in set value
*
*/
static uint32_t set_first_connection_operating_channel(
hdd_context_t *hdd_ctx, uint32_t set_value,
enum tQDF_ADAPTER_MODE dev_mode)
{
uint8_t operating_channel;
operating_channel = hdd_get_operating_channel(
hdd_ctx, dev_mode);
if (!operating_channel) {
hdd_err(" First adpter operating channel is invalid");
return -EINVAL;
}
hdd_info("First connection channel No.:%d and quota:%dms",
operating_channel, set_value);
/* Move the time quota for first channel to bits 15-8 */
set_value = set_value << 8;
/*
* Store the channel number of 1st channel at bits 7-0
* of the bit vector
*/
return set_value | operating_channel;
}
/**
* set_second_connection_operating_channel() - Function to set
* second connection oerating channel
* @adapter: adapter data
* @set_value: Quota value for the interface
* @vdev_id: vdev id
*
* This function is used to set the first adapter operating
* channel
*
* Return: operating channel updated in set value
*
*/
static uint32_t set_second_connection_operating_channel(
hdd_context_t *hdd_ctx, uint32_t set_value,
uint8_t vdev_id)
{
uint8_t operating_channel;
operating_channel = policy_mgr_get_mcc_operating_channel(
hdd_ctx->hdd_psoc, vdev_id);
if (operating_channel == 0) {
hdd_err("Second adapter operating channel is invalid");
return -EINVAL;
}
hdd_info("Second connection channel No.:%d and quota:%dms",
operating_channel, set_value);
/*
* Now move the time quota and channel number of the
* 1st adapter to bits 23-16 and bits 15-8 of the bit
* vector, respectively.
*/
set_value = set_value << 8;
/*
* Set the channel number for 2nd MCC vdev at bits
* 7-0 of set_value
*/
return set_value | operating_channel;
}
/**
* wlan_hdd_set_mcc_p2p_quota() - Function to set quota for P2P
* @psoc: PSOC object information
* @set_value: Qouta value for the interface
* @operating_channel First adapter operating channel
* @vdev_id vdev id
*
* This function is used to set the quota for P2P cases
*
* Return: Configuration message posting status, SUCCESS or Fail
*
*/
int wlan_hdd_set_mcc_p2p_quota(hdd_adapter_t *adapter,
uint32_t set_value)
{
int32_t ret = 0;
uint32_t concurrent_state;
hdd_context_t *hdd_ctx;
if (!adapter) {
hdd_err("Invalid adapter");
return -EFAULT;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (hdd_ctx == NULL) {
hdd_err("HDD context is null");
return -EINVAL;
}
concurrent_state = policy_mgr_get_concurrency_mode(
hdd_ctx->hdd_psoc);
/*
* Check if concurrency mode is active.
* Need to modify this code to support MCC modes other than STA/P2P
*/
if ((concurrent_state ==
(QDF_STA_MASK | QDF_P2P_CLIENT_MASK)) ||
(concurrent_state == (QDF_STA_MASK | QDF_P2P_GO_MASK))) {
hdd_info("STA & P2P are both enabled");
/*
* The channel numbers for both adapters and the time
* quota for the 1st adapter, i.e., one specified in cmd
* are formatted as a bit vector then passed on to WMA
* +***********************************************************+
* |bit 31-24 | bit 23-16 | bits 15-8 | bits 7-0 |
* | Unused | Quota for | chan. # for | chan. # for |
* | | 1st chan. | 1st chan. | 2nd chan. |
* +***********************************************************+
*/
set_value = set_first_connection_operating_channel(
hdd_ctx, set_value, adapter->device_mode);
set_value = set_second_connection_operating_channel(
hdd_ctx, set_value, adapter->sessionId);
ret = wlan_hdd_send_p2p_quota(adapter, set_value);
} else {
hdd_info("MCC is not active. Exit w/o setting latency");
}
return ret;
}
int wlan_hdd_go_set_mcc_p2p_quota(hdd_adapter_t *hostapd_adapter,
uint32_t set_value)
{
return wlan_hdd_set_mcc_p2p_quota(hostapd_adapter, set_value);
}
void wlan_hdd_set_mcc_latency(hdd_adapter_t *adapter, int set_value)
{
uint32_t concurrent_state;
hdd_context_t *hdd_ctx;
if (!adapter) {
hdd_err("Invalid adapter");
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (hdd_ctx == NULL) {
hdd_err("HDD context is null");
return;
}
concurrent_state = policy_mgr_get_concurrency_mode(
hdd_ctx->hdd_psoc);
/**
* Check if concurrency mode is active.
* Need to modify this code to support MCC modes other than STA/P2P
*/
if ((concurrent_state ==
(QDF_STA_MASK | QDF_P2P_CLIENT_MASK)) ||
(concurrent_state == (QDF_STA_MASK | QDF_P2P_GO_MASK))) {
hdd_info("STA & P2P are both enabled");
/*
* The channel number and latency are formatted in
* a bit vector then passed on to WMA layer.
* +**********************************************+
* |bits 31-16 | bits 15-8 | bits 7-0 |
* | Unused | latency - Chan. 1 | channel no. |
* +**********************************************+
*/
set_value = set_first_connection_operating_channel(
hdd_ctx, set_value, adapter->device_mode);
wlan_hdd_send_mcc_latency(adapter, set_value);
} else {
hdd_info("MCC is not active. Exit w/o setting latency");
}
}