blob: 188717275d7be2e23ed97a74526afb2b6a34b410 [file] [log] [blame]
/*
* Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**
*
* @file wlan_hdd_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 "cds_concurrency.h"
#include "cds_utils.h"
/* Ms to Time Unit Micro Sec */
#define MS_TO_TU_MUS(x) ((x) * 1024)
#define MAX_MUS_VAL (INT_MAX / 1024)
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";
}
}
#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"};
static bool 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 false;
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, uint32_t len)
{
enum action_frm_type 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 true;
}
return false;
}
/**
* hdd_is_p2p_go_cnf_frame() - function to if the frame type is go neg cnf
* @buf: pointer to frame
* @len: frame length
*
* This function is used to check if the given frame is GO negotiation
* confirmation frame.
*
* Return: true if the frame is go negotiation confirmation otherwise false
*/
static bool hdd_is_p2p_go_cnf_frame(const u8 *buf, uint32_t len)
{
if (wlan_hdd_is_type_p2p_action(buf, len) &&
buf[WLAN_HDD_PUBLIC_ACTION_FRAME_SUB_TYPE_OFFSET] ==
WLAN_HDD_GO_NEG_CNF)
return true;
else
return false;
}
/**
* hdd_random_mac_callback() - Callback invoked from wmi layer
* @set_random_addr: Status of random mac filter set operation
* @context: Context passed while registring callback
*
* This function is invoked from wmi layer to give the status of
* random mac filter set operation by firmware.
*
* Return: None
*/
static void hdd_random_mac_callback(bool set_random_addr, void *context)
{
struct random_mac_context *rnd_ctx;
hdd_adapter_t *adapter;
if (!context) {
hdd_err("Bad param, pContext");
return;
}
rnd_ctx = context;
adapter = rnd_ctx->adapter;
spin_lock(&hdd_context_lock);
if ((!adapter) ||
(rnd_ctx->magic != ACTION_FRAME_RANDOM_CONTEXT_MAGIC)) {
spin_unlock(&hdd_context_lock);
hdd_err("Invalid context, magic [%08x]", rnd_ctx->magic);
return;
}
rnd_ctx->magic = 0;
if (set_random_addr)
rnd_ctx->set_random_addr = true;
complete(&rnd_ctx->random_mac_completion);
spin_unlock(&hdd_context_lock);
}
/**
* hdd_set_random_mac() - Invoke sme api to set random mac filter
* @adapter: Pointer to adapter
* @random_mac_addr: Mac addr filter to be set
* @freq: Channel frequency
*
* Return: If set is successful return true else return false
*/
static bool hdd_set_random_mac(hdd_adapter_t *adapter,
uint8_t *random_mac_addr,
uint32_t freq)
{
struct random_mac_context context;
hdd_context_t *hdd_ctx;
QDF_STATUS sme_status;
unsigned long rc;
bool status = false;
ENTER();
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (wlan_hdd_validate_context(hdd_ctx)) {
hdd_err("Invalid hdd ctx");
return false;
}
init_completion(&context.random_mac_completion);
context.adapter = adapter;
context.magic = ACTION_FRAME_RANDOM_CONTEXT_MAGIC;
context.set_random_addr = false;
sme_status = sme_set_random_mac(hdd_ctx->hHal, hdd_random_mac_callback,
adapter->sessionId, random_mac_addr, freq,
&context);
if (sme_status != QDF_STATUS_SUCCESS) {
hdd_err("Unable to set random mac");
} else {
rc = wait_for_completion_timeout(&context.random_mac_completion,
msecs_to_jiffies(WLAN_WAIT_TIME_SET_RND));
if (!rc)
hdd_err("SME timed out while setting random mac");
}
spin_lock(&hdd_context_lock);
context.magic = 0;
status = context.set_random_addr;
spin_unlock(&hdd_context_lock);
EXIT();
return status;
}
/**
* hdd_clear_random_mac() - Invoke sme api to clear random mac filter
* @adapter: Pointer to adapter
* @random_mac_addr: Mac addr filter to be cleared
* @freq: Channel frequency
*
* Return: If clear is successful return true else return false
*/
static bool hdd_clear_random_mac(hdd_adapter_t *adapter,
uint8_t *random_mac_addr,
uint32_t freq)
{
hdd_context_t *hdd_ctx;
QDF_STATUS status;
ENTER();
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (wlan_hdd_validate_context(hdd_ctx)) {
hdd_err("Invalid hdd ctx");
return false;
}
status = sme_clear_random_mac(hdd_ctx->hHal, adapter->sessionId,
random_mac_addr, freq);
if (status != QDF_STATUS_SUCCESS) {
hdd_err("Unable to clear random mac");
return false;
}
EXIT();
return true;
}
bool hdd_check_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr)
{
uint32_t i = 0;
spin_lock(&adapter->random_mac_lock);
for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
if ((adapter->random_mac[i].in_use) &&
(!qdf_mem_cmp(adapter->random_mac[i].addr, random_mac_addr,
QDF_MAC_ADDR_SIZE))) {
spin_unlock(&adapter->random_mac_lock);
return true;
}
}
spin_unlock(&adapter->random_mac_lock);
return false;
}
/**
* find_action_frame_cookie() - Checks for action cookie in cookie list
* @cookie_list: List of cookies
* @cookie: Cookie to be searched
*
* Return: If search is successful return pointer to action_frame_cookie
* object in which cookie item is encapsulated.
*/
static struct action_frame_cookie *find_action_frame_cookie(
struct list_head *cookie_list,
uint64_t cookie)
{
struct action_frame_cookie *action_cookie = NULL;
struct list_head *temp = NULL;
list_for_each(temp, cookie_list) {
action_cookie = list_entry(temp, struct action_frame_cookie,
cookie_node);
if (action_cookie->cookie == cookie)
return action_cookie;
}
return NULL;
}
/**
* allocate_action_frame_cookie() - Allocate and add action cookie to
* given list
* @cookie_list: List of cookies
* @cookie: Cookie to be added
*
* Return: If allocation and addition is successful return pointer to
* action_frame_cookie object in which cookie item is encapsulated.
*/
static struct action_frame_cookie *allocate_action_frame_cookie(
struct list_head *cookie_list,
uint64_t cookie)
{
struct action_frame_cookie *action_cookie = NULL;
action_cookie = qdf_mem_malloc(sizeof(*action_cookie));
if (!action_cookie)
return NULL;
action_cookie->cookie = cookie;
list_add(&action_cookie->cookie_node, cookie_list);
return action_cookie;
}
/**
* delete_action_frame_cookie() - Delete the cookie from given list
* @cookie_list: List of cookies
* @cookie: Cookie to be deleted
*
* This function deletes the cookie item from given list and corresponding
* object in which it is encapsulated.
*
* Return: None
*/
static void delete_action_frame_cookie(
struct action_frame_cookie *action_cookie)
{
list_del(&action_cookie->cookie_node);
qdf_mem_free(action_cookie);
}
/**
* append_action_frame_cookie() - Append action cookie to given list
* @cookie_list: List of cookies
* @cookie: Cookie to be append
*
* This is a wrapper function which invokes allocate_action_frame_cookie
* if the cookie to be added is not duplicate
*
* Return: 0 - for successful case
* -EALREADY - if cookie is duplicate
* -ENOMEM - if allocation is failed
*/
static int32_t append_action_frame_cookie(struct list_head *cookie_list,
uint64_t cookie)
{
struct action_frame_cookie *action_cookie = NULL;
/*
* There should be no mac entry with empty cookie list,
* check and ignore if duplicate
*/
action_cookie = find_action_frame_cookie(cookie_list, cookie);
if (action_cookie)
/* random mac address is already programmed */
return -EALREADY;
/* insert new cookie in cookie list */
action_cookie = allocate_action_frame_cookie(cookie_list, cookie);
if (!action_cookie)
return -ENOMEM;
return 0;
}
/**
* hdd_set_action_frame_random_mac() - Store action frame cookie
* @adapter: Pointer to adapter
* @random_mac_addr: Mac addr for cookie
* @cookie: Cookie to be stored
* @freq: Channel frequency
*
* This function is used to create cookie list and append the cookies
* to same for corresponding random mac addr. If this cookie is the first
* item in the list then random mac filter is set.
*
* Return: 0 - for success else negative value
*/
static int32_t hdd_set_action_frame_random_mac(hdd_adapter_t *adapter,
uint8_t *random_mac_addr,
uint64_t cookie, uint32_t freq)
{
uint32_t i = 0;
uint32_t first_unused = MAX_RANDOM_MAC_ADDRS;
struct action_frame_cookie *action_cookie = NULL;
int32_t append_ret = 0;
if (!cookie) {
hdd_err("Invalid cookie");
return -EINVAL;
}
hdd_info("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu && freq = %u",
MAC_ADDR_ARRAY(random_mac_addr), cookie, freq);
spin_lock(&adapter->random_mac_lock);
/*
* Following loop checks whether random mac entry is already
* present, if present get the index of matched entry else
* get the first unused slot to store this new random mac
*/
for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
if (!adapter->random_mac[i].in_use) {
if (first_unused == MAX_RANDOM_MAC_ADDRS)
first_unused = i;
continue;
}
if (!qdf_mem_cmp(adapter->random_mac[i].addr, random_mac_addr,
QDF_MAC_ADDR_SIZE))
break;
}
if (i != MAX_RANDOM_MAC_ADDRS) {
append_ret = append_action_frame_cookie(
&adapter->random_mac[i].cookie_list,
cookie);
spin_unlock(&adapter->random_mac_lock);
if (append_ret == -ENOMEM) {
hdd_err("No Sufficient memory for cookie");
return append_ret;
}
return 0;
}
if (first_unused == MAX_RANDOM_MAC_ADDRS) {
spin_unlock(&adapter->random_mac_lock);
hdd_err("Reached the limit of Max random addresses");
return -EBUSY;
}
/* get the first unused buf and store new random mac */
i = first_unused;
INIT_LIST_HEAD(&adapter->random_mac[i].cookie_list);
action_cookie = allocate_action_frame_cookie(
&adapter->random_mac[i].cookie_list,
cookie);
if (!action_cookie) {
spin_unlock(&adapter->random_mac_lock);
hdd_err("No Sufficient memory for cookie");
return -ENOMEM;
}
qdf_mem_copy(adapter->random_mac[i].addr, random_mac_addr,
QDF_MAC_ADDR_SIZE);
adapter->random_mac[i].in_use = true;
adapter->random_mac[i].freq = freq;
spin_unlock(&adapter->random_mac_lock);
/* Program random mac_addr */
if (!hdd_set_random_mac(adapter, adapter->random_mac[i].addr, freq)) {
spin_lock(&adapter->random_mac_lock);
/* clear the cookie */
delete_action_frame_cookie(action_cookie);
adapter->random_mac[i].in_use = false;
spin_unlock(&adapter->random_mac_lock);
hdd_err("random mac filter set failed for: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
return -EFAULT;
}
return 0;
}
/**
* hdd_reset_action_frame_random_mac() - Delete action frame cookie with
* given random mac addr
* @adapter: Pointer to adapter
* @random_mac_addr: Mac addr for cookie
* @cookie: Cookie to be deleted
*
* This function is used to delete the cookie from the cookie list
* corresponding to given random mac addr.If cookie list is empty after
* deleting, it will clear random mac filter.
*
* Return: 0 - for success else negative value
*/
static int32_t hdd_reset_action_frame_random_mac(hdd_adapter_t *adapter,
uint8_t *random_mac_addr,
uint64_t cookie)
{
uint32_t i = 0;
uint32_t freq = 0;
struct action_frame_cookie *action_cookie = NULL;
if (!cookie) {
hdd_err("Invalid cookie");
return -EINVAL;
}
hdd_info("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu",
MAC_ADDR_ARRAY(random_mac_addr), cookie);
spin_lock(&adapter->random_mac_lock);
for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
if ((adapter->random_mac[i].in_use) &&
(!qdf_mem_cmp(adapter->random_mac[i].addr,
random_mac_addr, QDF_MAC_ADDR_SIZE)))
break;
}
if (i == MAX_RANDOM_MAC_ADDRS) {
/*
* trying to delete cookie of random mac-addr
* for which entry is not present
*/
spin_unlock(&adapter->random_mac_lock);
return -EINVAL;
}
action_cookie = find_action_frame_cookie(
&adapter->random_mac[i].cookie_list,
cookie);
if (!action_cookie) {
spin_unlock(&adapter->random_mac_lock);
hdd_info("No cookie matches");
return 0;
}
delete_action_frame_cookie(action_cookie);
if (list_empty(&adapter->random_mac[i].cookie_list)) {
adapter->random_mac[i].in_use = false;
freq = adapter->random_mac[i].freq;
spin_unlock(&adapter->random_mac_lock);
hdd_clear_random_mac(adapter, random_mac_addr,
freq);
hdd_info("Deleted random mac_addr: "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(random_mac_addr));
return 0;
}
spin_unlock(&adapter->random_mac_lock);
return 0;
}
/**
* hdd_delete_action_frame_cookie() - Delete action frame cookie
* @adapter: Pointer to adapter
* @cookie: Cookie to be deleted
*
* This function parses the cookie list of each random mac addr until the
* specified cookie is found and then deletes it. If cookie list is empty
* after deleting, it will clear random mac filter.
*
* Return: 0 - for success else negative value
*/
static int32_t hdd_delete_action_frame_cookie(hdd_adapter_t *adapter,
uint64_t cookie)
{
uint32_t i = 0;
struct action_frame_cookie *action_cookie = NULL;
uint32_t freq = 0;
hdd_debug("Delete cookie = %llu", cookie);
spin_lock(&adapter->random_mac_lock);
for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
if (!adapter->random_mac[i].in_use)
continue;
action_cookie = find_action_frame_cookie(
&adapter->random_mac[i].cookie_list,
cookie);
if (!action_cookie)
continue;
delete_action_frame_cookie(action_cookie);
if (list_empty(&adapter->random_mac[i].cookie_list)) {
adapter->random_mac[i].in_use = false;
freq = adapter->random_mac[i].freq;
spin_unlock(&adapter->random_mac_lock);
hdd_clear_random_mac(adapter,
adapter->random_mac[i].addr,
freq);
hdd_info("Deleted random addr "MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
return 0;
}
spin_unlock(&adapter->random_mac_lock);
return 0;
}
spin_unlock(&adapter->random_mac_lock);
return 0;
}
/**
* hdd_delete_all_action_frame_cookies() - Delete all action frame cookies
* @adapter: Pointer to adapter
*
* This function deletes all the cookie lists of each random mac addr and
* clears the corresponding random mac filters.
*
* Return: 0 - for success else negative value
*/
static void hdd_delete_all_action_frame_cookies(hdd_adapter_t *adapter)
{
uint32_t i = 0;
struct action_frame_cookie *action_cookie = NULL;
struct list_head *n;
struct list_head *temp;
uint32_t freq = 0;
spin_lock(&adapter->random_mac_lock);
for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
if (!adapter->random_mac[i].in_use)
continue;
/* empty the list and clear random addr */
list_for_each_safe(temp, n,
&adapter->random_mac[i].cookie_list) {
action_cookie = list_entry(temp,
struct action_frame_cookie,
cookie_node);
list_del(temp);
qdf_mem_free(action_cookie);
}
adapter->random_mac[i].in_use = false;
freq = adapter->random_mac[i].freq;
spin_unlock(&adapter->random_mac_lock);
hdd_clear_random_mac(adapter, adapter->random_mac[i].addr,
freq);
hdd_info("Deleted random addr " MAC_ADDRESS_STR,
MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
spin_lock(&adapter->random_mac_lock);
}
spin_unlock(&adapter->random_mac_lock);
}
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_warn("Failed to stop hdd_remain_on_chan_timer");
if (qdf_mc_timer_destroy(&pRemainChanCtx->hdd_remain_on_chan_timer)
!= QDF_STATUS_SUCCESS)
hdd_warn("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);
}
hdd_delete_all_action_frame_cookies(pAdapter);
} 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;
}
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_warn("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_warn("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)
) {
hdd_delete_all_action_frame_cookies(pAdapter);
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 for cancel ROC 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);
}
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
*/
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) {
void *sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(adapter);
wlansap_cancel_remain_on_channel(sap_ctx, roc_scan_id);
} else if (adapter->device_mode == QDF_P2P_CLIENT_MODE ||
adapter->device_mode == QDF_P2P_DEVICE_MODE) {
hdd_delete_all_action_frame_cookies(adapter);
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);
}
}
/* Clean up RoC context at hdd_stop_adapter*/
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);
}
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 %pK !!!", 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)) {
hdd_delete_all_action_frame_cookies(pAdapter);
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)) {
void *sap_ctx = WLAN_HDD_GET_SAP_CTX_PTR(pAdapter);
wlansap_cancel_remain_on_channel(sap_ctx, 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=%pK",
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;
}
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
if ((pRemainChanCtx) && (REMAIN_ON_CHANNEL_REQUEST ==
pRemainChanCtx->rem_on_chan_request)) {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
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 {
mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
}
} 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=%pK",
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
*/
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;
}
/**
* 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);
}
/**
* wlan_hdd_is_roc_in_progress_for_other_adapters() - Check if roc is in
* progress for another adapter
* @hdd_ctx: HDD context
* @cur_adapter: current adapter
*
* Roc requests are serialized per adapter. This means that simultaneous
* roc requests on multiple adapters are not supported. This function checks
* and returns if there is an roc being executed on another adapter.
*
* Return: true if roc is ongoing for another adapter, false otherwise.
*/
static bool
wlan_hdd_is_roc_in_progress_for_other_adapters(hdd_context_t *hdd_ctx,
hdd_adapter_t *cur_adapter)
{
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
hdd_adapter_t *adapter;
QDF_STATUS qdf_status;
qdf_status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while ((NULL != adapter_node) && (QDF_STATUS_SUCCESS == qdf_status)) {
adapter = adapter_node->pAdapter;
if (cur_adapter != adapter) {
if (adapter->is_roc_inprogress)
return true;
}
qdf_status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
return false;
}
/**
* wlan_hdd_is_roc_req_queued_by_other_adapters() - Check if an roc req is
* queued by another adapter
* @hdd_ctx: HDD context
* @cur_adapter: current adapter
*
* Roc requests are serialized per adapter. This means that simultaneous
* roc requests on multiple adapters are not supported. This function checks
* and returns if there is an roc request queued by another adapter.
*
* Return: true if roc is queued by another adapter, false otherwise.
*/
static bool
wlan_hdd_is_roc_req_queued_by_other_adapters(hdd_context_t *hdd_ctx,
hdd_adapter_t *cur_adapter)
{
qdf_list_node_t *node = NULL, *next_node = NULL;
hdd_roc_req_t *roc_req;
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 false;
}
if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&hdd_ctx->hdd_roc_req_q,
&next_node)) {
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
hdd_err("Unable to peek roc element from list");
return false;
}
do {
node = next_node;
roc_req = qdf_container_of(node, hdd_roc_req_t, node);
if (roc_req->pAdapter != cur_adapter) {
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
return true;
}
} while (QDF_STATUS_SUCCESS == qdf_list_peek_next(
&hdd_ctx->hdd_roc_req_q,
node, &next_node));
qdf_spin_unlock(&hdd_ctx->hdd_roc_req_q_lock);
return false;
}
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 ((wlan_hdd_is_roc_in_progress_for_other_adapters(pHddCtx, pAdapter))
|| (wlan_hdd_is_roc_req_queued_by_other_adapters(pHddCtx, pAdapter))
) {
hdd_debug("ROC in progress or queued for another adapter");
return -EAGAIN;
}
if (cds_is_connection_in_progress(NULL, NULL)) {
hdd_debug("Connection is in progress");
if (request_type == OFF_CHANNEL_ACTION_TX) {
hdd_debug("Reject Offchannel action frame tx as conection in progress");
return -EAGAIN;
}
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 %pK",
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;
}
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;
}
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_debug("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);
}
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 (true ==
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;
hdd_delete_all_action_frame_cookies(pAdapter);
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;
}
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;
}
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;
uint8_t subType;
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;
bool enb_random_mac = false;
uint32_t mgmt_hdr_len = sizeof(struct ieee80211_hdr_3addr);
ENTER();
if (len < mgmt_hdr_len + 1) {
hdd_err("Invalid Length");
return -EINVAL;
}
type = WLAN_HDD_GET_TYPE_FRM_FC(buf[0]);
subType = WLAN_HDD_GET_SUBTYPE_FRM_FC(buf[0]);
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",
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode, type, wait, offchan);
if (type == SIR_MAC_MGMT_FRAME && subType == SIR_MAC_MGMT_ACTION &&
len > IEEE80211_MIN_ACTION_SIZE)
hdd_debug("category: %d, actionId: %d",
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],
len - mgmt_hdr_len)) {
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 top2p 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_warn("Previous P2P Action frame packet pending");
if (!hdd_is_p2p_go_cnf_frame(buf, len))
hdd_cleanup_actionframe(pAdapter->pHddCtx,
pAdapter);
else {
hdd_cleanup_actionframe_no_wait(
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],
len - mgmt_hdr_len)
&& 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_warn("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))) {
mutex_unlock(
&cfgState->remain_on_chan_ctx_lock);
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");
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
}
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],
len - mgmt_hdr_len)) {
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_mem_cmp((uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
&pAdapter->macAddressCurrent, QDF_MAC_ADDR_SIZE)) {
hdd_info("%s: action frame sa is randomized with mac: "
MAC_ADDRESS_STR, __func__,
MAC_ADDR_ARRAY((uint8_t *)
(&buf[WLAN_HDD_80211_FRM_SA_OFFSET])));
enb_random_mac = true;
}
if (enb_random_mac && !noack && chan && chan->center_freq)
hdd_set_action_frame_random_mac(pAdapter,
(uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
*cookie, chan->center_freq);
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) {
if (enb_random_mac && chan && chan->center_freq &&
((pAdapter->device_mode == QDF_STA_MODE) ||
(pAdapter->device_mode == QDF_P2P_CLIENT_MODE) ||
(pAdapter->device_mode == QDF_P2P_DEVICE_MODE)))
hdd_reset_action_frame_random_mac(pAdapter,
(uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
*cookie);
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;
}
#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;
}
/**
* hdd_wlan_delete_mgmt_tx_cookie() - Wrapper to delete action frame cookie
* @wdev: Pointer to wireless device
* @cookie: Cookie to be deleted
*
* This is a wrapper function which actually invokes the hdd api to delete
* cookie based on the device mode of adapter.
*
* Return: 0 - for success else negative value
*/
static int hdd_wlan_delete_mgmt_tx_cookie(struct wireless_dev *wdev,
u64 cookie)
{
struct net_device *dev = wdev->netdev;
hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
if ((adapter->device_mode == QDF_STA_MODE) ||
(adapter->device_mode == QDF_P2P_CLIENT_MODE) ||
(adapter->device_mode == QDF_P2P_DEVICE_MODE)) {
hdd_delete_action_frame_cookie(adapter, cookie);
}
return 0;
}
static int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
u64 cookie)
{
hdd_wlan_delete_mgmt_tx_cookie(wdev, cookie);
return wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, wdev, cookie);
}
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);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
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);
sme_p2p_set_ps(hHal, &NoA);
return 0;
}
/**
* 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);
tHalHandle handle = WLAN_HDD_GET_HAL_CTX(adapter);
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);
sme_p2p_set_ps(handle, &noa);
}
return 0;
}
int hdd_set_p2p_ps(struct net_device *dev, void *msgData)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
tHalHandle hHal = WLAN_HDD_GET_HAL_CTX(pAdapter);
QDF_STATUS status = QDF_STATUS_SUCCESS;
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;
sme_p2p_set_ps(hHal, &NoA);
return status;
}
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_allow_sap_add() - check to add new sap interface
* @hdd_ctx: pointer to hdd context
* @name: name of the new interface
* @sap_dev: output pointer to hold existing interface
*
* Return: If able to add interface return true else false
*/
static bool
wlan_hdd_allow_sap_add(hdd_context_t *hdd_ctx,
const char *name,
struct wireless_dev **sap_dev)
{
hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
QDF_STATUS status;
hdd_adapter_t *adapter;
*sap_dev = NULL;
status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
while (adapter_node && QDF_IS_STATUS_SUCCESS(status)) {
adapter = adapter_node->pAdapter;
if (adapter && adapter->device_mode == QDF_SAP_MODE &&
test_bit(NET_DEVICE_REGISTERED, &adapter->event_flags) &&
!strncmp(adapter->dev->name, name, IFNAMSIZ)) {
beacon_data_t *beacon = adapter->sessionCtx.ap.beacon;
hdd_debug("iface already registered");
if (beacon) {
adapter->sessionCtx.ap.beacon = NULL;
qdf_mem_free(beacon);
}
if (adapter->dev && adapter->dev->ieee80211_ptr) {
*sap_dev = adapter->dev->ieee80211_ptr;
return false;
}
hdd_err("ieee80211_ptr points to NULL");
return false;
}
status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
adapter_node = next;
}
return true;
}
/**
* __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) {
hdd_abort_mac_scan(pHddCtx, pAdapter->sessionId,
INVALID_SCAN_ID,
eCSR_SCAN_ABORT_DEFAULT);
hdd_debug("Abort Scan while adding virtual interface");
}
}
if (session_type == QDF_SAP_MODE) {
struct wireless_dev *sap_dev;
bool allow_add_sap = wlan_hdd_allow_sap_add(pHddCtx, name,
&sap_dev);
if (!allow_add_sap) {
if (sap_dev)
return sap_dev;
return ERR_PTR(-EINVAL);
}
}
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,
session_type),
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_sched_delayed_work(&pHddCtx->iface_idle_work,
pHddCtx->config->iface_change_wait_time);
hdd_prevent_suspend_timeout(
pHddCtx->config->iface_change_wait_time,
WIFI_POWER_EVENT_WAKELOCK_IFACE_CHANGE_TIMER);
} 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, 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;
cds_ssr_protect(__func__);
wdev = __wlan_hdd_add_virtual_intf(wiphy, name, name_assign_type,
type, &params->flags, params);
cds_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;
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;
}
/*
* Clear SOFTAP_INIT_DONE flag to mark SAP unload, so that we do
* not restart SAP after SSR as SAP is already stopped from user space.
*/
clear_bit(SOFTAP_INIT_DONE, &pVirtAdapter->event_flags);
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
static inline bool is_non_probe_req_mgmt_frame(uint8_t type, uint8_t sub_type)
{
return type == SIR_MAC_MGMT_FRAME &&
sub_type != SIR_MAC_MGMT_PROBE_REQ;
}
static hdd_adapter_t *get_adapter_from_broadcast_ocb(hdd_context_t *hdd_ctx,
uint8_t *broadcast)
{
hdd_adapter_t *adapter;
adapter = hdd_get_adapter(hdd_ctx, QDF_OCB_MODE);
if (!adapter) {
hdd_err("Dropping the frame. No frame with BCST as destination");
return NULL;
}
if (broadcast) {
hdd_debug("Set broadcast to true");
*broadcast = 1;
}
return adapter;
}
static hdd_adapter_t *get_adapter_from_mac_addr(hdd_context_t *hdd_ctx,
uint8_t *dest_addr)
{
hdd_adapter_t *adapter;
adapter = hdd_get_adapter_by_macaddr(hdd_ctx, dest_addr);
return (adapter) ? adapter :
hdd_get_adapter_by_rand_macaddr(hdd_ctx, dest_addr);
}
static inline bool is_mgmt_non_broadcast_frame(uint8_t type,
uint8_t sub_type,
uint8_t broadcast)
{
return (type == SIR_MAC_MGMT_FRAME) &&
(sub_type == SIR_MAC_MGMT_ACTION) && !broadcast;
}
static inline bool is_public_action_frame(uint8_t *pb_frames,
uint32_t frame_len)
{
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET) {
hdd_debug("Not a public action frame len: %d", frame_len);
return false;
}
return (pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] ==
WLAN_HDD_PUBLIC_ACTION_FRAME);
}
static inline bool is_p2p_action_frame(uint8_t *pb_frames,
uint32_t frame_len)
{
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET +
SIR_MAC_P2P_OUI_SIZE) {
hdd_debug("Not a p2p action frame len: %d", frame_len);
return false;
}
if ((pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1]
!= SIR_MAC_ACTION_VENDOR_SPECIFIC))
return false;
return !qdf_mem_cmp(&pb_frames
[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 2],
SIR_MAC_P2P_OUI, SIR_MAC_P2P_OUI_SIZE);
}
static inline bool is_tdls_action_frame(uint8_t *pb_frames,
uint32_t frame_len)
{
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET) {
hdd_debug("Not a TDLS action frame len: %d", frame_len);
return false;
}
return (pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] ==
WLAN_HDD_TDLS_ACTION_FRAME);
}
static inline bool is_tdls_disc_resp_frame(uint8_t *pb_frames,
uint32_t frame_len)
{
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1) {
hdd_debug("Not a TDLS disc resp frame len: %d", frame_len);
return false;
}
return (pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1] ==
WLAN_HDD_PUBLIC_ACTION_TDLS_DISC_RESP);
}
static inline bool is_qos_action_frame(uint8_t *pb_frames, uint32_t frame_len)
{
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1) {
hdd_debug("Not a QOS frame len: %d", frame_len);
return false;
}
return ((pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET] ==
WLAN_HDD_QOS_ACTION_FRAME) &&
(pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1] ==
WLAN_HDD_QOS_MAP_CONFIGURE));
}
static inline bool is_extend_roc_timer_for_action(enum action_frm_type
frm_type)
{
switch (frm_type) {
case WLAN_HDD_GO_NEG_REQ:
case WLAN_HDD_GO_NEG_RESP:
case WLAN_HDD_INVITATION_REQ:
case WLAN_HDD_DEV_DIS_REQ:
case WLAN_HDD_PROV_DIS_REQ:
return true;
default:
return false;
}
}
static inline int get_roc_extension_time(enum action_frm_type frm_type)
{
switch (frm_type) {
case WLAN_HDD_GO_NEG_REQ:
case WLAN_HDD_GO_NEG_RESP:
return 2 * ACTION_FRAME_DEFAULT_WAIT;
default:
return ACTION_FRAME_DEFAULT_WAIT;
}
}
static int extend_roc_timer(hdd_remain_on_chan_ctx_t *rem_chan_ctx,
uint16_t extend_time)
{
QDF_STATUS status;
if (!rem_chan_ctx) {
hdd_err("Extend ROC timer failed");
return -EINVAL;
}
if (QDF_TIMER_STATE_RUNNING !=
qdf_mc_timer_get_current_state(
&rem_chan_ctx->hdd_remain_on_chan_timer)) {
hdd_debug("Rcvd action frame after timer expired");
return -EFAULT;
}
qdf_mc_timer_stop(&rem_chan_ctx->hdd_remain_on_chan_timer);
status = qdf_mc_timer_start(&rem_chan_ctx->hdd_remain_on_chan_timer,
extend_time);
if (QDF_STATUS_SUCCESS != status) {
hdd_err("Remain on Channel timer start failed");
return -EFAULT;
}
return 0;
}
static void buffer_p2p_action_frame(hdd_remain_on_chan_ctx_t *rem_chan_ctx,
uint8_t *pb_frames, uint32_t frm_len,
uint16_t freq)
{
if (!rem_chan_ctx) {
hdd_err("Buffer P2P action frame failed");
return;
}
if (rem_chan_ctx->action_pkt_buff.frame_length != 0) {
hdd_warn("Frames are pending. dropping frame !!!");
return;
}
rem_chan_ctx->action_pkt_buff.frame_length = frm_len;
rem_chan_ctx->action_pkt_buff.freq = freq;
rem_chan_ctx->action_pkt_buff.frame_ptr =
qdf_mem_malloc(frm_len);
qdf_mem_copy(rem_chan_ctx->action_pkt_buff.frame_ptr, pb_frames,
frm_len);
hdd_debug("Action Pkt Cached successfully !!!");
}
static inline bool is_rx_action_resp_while_ack_pending(
enum p2p_action_frame_state frame_state,
enum action_frm_type frm_type)
{
return (frm_type == WLAN_HDD_PROV_DIS_RESP &&
frame_state == HDD_PD_REQ_ACK_PENDING) ||
(frm_type == WLAN_HDD_GO_NEG_RESP &&
frame_state == HDD_GO_NEG_REQ_ACK_PENDING);
}
static bool process_rx_p2p_action_frame(hdd_adapter_t *adapter,
uint8_t *pb_frames,
hdd_cfg80211_state_t *cfg_state,
uint32_t frm_len, uint16_t freq)
{
uint8_t *macFrom;
enum action_frm_type frm_type;
uint16_t extend_time;
hdd_remain_on_chan_ctx_t *rem_chan_ctx = NULL;
macFrom = &pb_frames[WLAN_HDD_80211_PEER_ADDR_OFFSET];
frm_type = pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_TYPE_OFFSET];
hdd_debug("Rx Action Frame %u", frm_type);
wlan_hdd_p2p_action_debug(frm_type, macFrom);
mutex_lock(&cfg_state->remain_on_chan_ctx_lock);
rem_chan_ctx = cfg_state->remain_on_chan_ctx;
if (rem_chan_ctx) {
if (is_extend_roc_timer_for_action(frm_type)) {
extend_time = get_roc_extension_time(frm_type);
hdd_debug("Extend RoC timer on reception of Action Frame %d",
extend_time);
if (completion_done(
&adapter->rem_on_chan_ready_event)) {
extend_roc_timer(rem_chan_ctx, extend_time);
} else {
hdd_err("Buffer packet");
buffer_p2p_action_frame(rem_chan_ctx,
pb_frames,
frm_len, freq);
mutex_unlock(&cfg_state->
remain_on_chan_ctx_lock);
return false;
}
}
}
mutex_unlock(&cfg_state->remain_on_chan_ctx_lock);
if (is_rx_action_resp_while_ack_pending(cfg_state->actionFrmState,
frm_type)) {
hdd_debug("ACK_PENDING and But received RESP for Action frame ");
cfg_state->is_go_neg_ack_received = 1;
hdd_send_action_cnf(adapter, true);
}
return true;
}
static void log_rx_tdls_action_frame(uint8_t *pb_frames, uint32_t frame_len)
{
enum action_frm_type frm_type;
if (frame_len <= WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1) {
hdd_debug("Not a TDLS action frame len: %d", frame_len);
return;
}
frm_type = pb_frames[WLAN_HDD_PUBLIC_ACTION_FRAME_OFFSET + 1];
if (frm_type >= MAX_TDLS_ACTION_FRAME_TYPE) {
hdd_debug("[TDLS] unknown[%d] <--- OTA",
frm_type);
} else {
hdd_debug("[TDLS] %s <--- OTA",
tdls_action_frame_type[frm_type]);
}
cds_tdls_tx_rx_mgmt_event(SIR_MAC_ACTION_TDLS, SIR_MAC_ACTION_RX,
SIR_MAC_MGMT_ACTION, frm_type,
&pb_frames[WLAN_HDD_80211_PEER_ADDR_OFFSET]);
}
#ifdef FEATURE_WLAN_TDLS
static void process_tdls_rx_action_frame(hdd_adapter_t *adapter,
uint8_t *peer_addr, int8_t rx_rssi)
{
process_rx_tdls_disc_resp_frame(adapter, peer_addr, rx_rssi);
}
#else
static void process_tdls_rx_action_frame(hdd_adapter_t *adapter,
uint8_t *peer_addr, int8_t rx_rssi)
{
}
#endif
static bool process_rx_public_action_frame(hdd_adapter_t *adapter,
uint8_t *pb_frames,
hdd_cfg80211_state_t *cfg_state,
enum action_frm_type frm_type,
uint32_t frm_len, uint16_t freq,
int8_t rx_rssi)
{
if (!adapter || !pb_frames || !cfg_state) {
hdd_err("Invalid parameter");
return false;
}
if (!is_public_action_frame(pb_frames, frm_len))
goto done;
if (is_p2p_action_frame(pb_frames, frm_len)) {
hdd_debug("Process P2P action frame");
return process_rx_p2p_action_frame(adapter, pb_frames,
cfg_state, frm_len, freq);
}
if (is_tdls_disc_resp_frame(pb_frames, frm_len)) {
uint8_t *peer_addr;
peer_addr = &pb_frames[WLAN_HDD_80211_PEER_ADDR_OFFSET];
process_tdls_rx_action_frame(adapter, peer_addr, rx_rssi);
}
done:
return true;
}
static uint16_t get_rx_frame_freq_from_chan(uint32_t rx_chan)
{
if (rx_chan <= MAX_NO_OF_2_4_CHANNELS)
return ieee80211_channel_to_frequency(rx_chan,
HDD_NL80211_BAND_2GHZ);
return ieee80211_channel_to_frequency(rx_chan,
HDD_NL80211_BAND_5GHZ);
}
static void indicate_rx_mgmt_over_nl80211(hdd_adapter_t *adapter,
uint32_t frm_len,
uint8_t *pb_frames, uint16_t freq,
int8_t rx_rssi)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr,
freq, rx_rssi * 100, pb_frames,
frm_len, NL80211_RXMGMT_FLAG_ANSWERED);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr,
freq, rx_rssi * 100, pb_frames,
frm_len, NL80211_RXMGMT_FLAG_ANSWERED,
GFP_ATOMIC);
#else
cfg80211_rx_mgmt(adapter->dev->ieee80211_ptr, freq,
rx_rssi * 100,
pb_frames, frm_len, GFP_ATOMIC);
#endif /* LINUX_VERSION_CODE */
}
void __hdd_indicate_mgmt_frame(hdd_adapter_t *adapter, uint32_t frm_len,
uint8_t *pb_frames, uint8_t frame_type,
uint32_t rx_chan, int8_t rx_rssi)
{
uint16_t freq;
uint8_t type = 0;
uint8_t sub_type = 0;
enum action_frm_type frm_type;
hdd_cfg80211_state_t *cfg_state;
hdd_context_t *hdd_ctx;
uint8_t broadcast = 0;
uint8_t *dest_addr;
hdd_debug("Frame Type = %d Frame Length = %d",
frame_type, frm_len);
if (NULL == adapter) {
hdd_err("adapter is NULL");
return;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (0 == frm_len) {
hdd_err("Frame Length is Invalid ZERO");
return;
}
if (NULL == pb_frames) {
hdd_err("pb_frames is NULL");
return;
}
type = WLAN_HDD_GET_TYPE_FRM_FC(pb_frames[0]);
sub_type = WLAN_HDD_GET_SUBTYPE_FRM_FC(pb_frames[0]);
hdd_debug("Frame Type = %d subType = %d", type, sub_type);
if (is_non_probe_req_mgmt_frame(type, sub_type) &&
!qdf_is_macaddr_broadcast(
(struct qdf_mac_addr *)&pb_frames[WLAN_HDD_80211_FRM_DA_OFFSET])) {
if (frm_len <= WLAN_HDD_80211_FRM_DA_OFFSET +
QDF_MAC_ADDR_SIZE) {
hdd_err("Cannot parse dest mac addr len: %d", frm_len);
return;
}
dest_addr = &pb_frames[WLAN_HDD_80211_FRM_DA_OFFSET];
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)
dest_addr)) {
hdd_debug("Check if rx mgmt OCB frames");
adapter = get_adapter_from_broadcast_ocb(hdd_ctx,
&broadcast);
} else {
adapter = get_adapter_from_mac_addr(hdd_ctx,
dest_addr);
}
if (!adapter) {
hdd_err("Cannot get adapter for RX mgmt frame");
return;
}
}
if (!adapter->dev) {
hdd_err("adapter->dev is NULL");
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != adapter->magic) {
hdd_err("adapter has invalid magic");
return;
}
freq = get_rx_frame_freq_from_chan(rx_chan);
cfg_state = WLAN_HDD_GET_CFG_STATE_PTR(adapter);
if (!is_mgmt_non_broadcast_frame(type, sub_type, broadcast))
goto indicate;
if (is_public_action_frame(pb_frames, frm_len)) {
bool processed;
processed = process_rx_public_action_frame(adapter, pb_frames,
cfg_state, frm_type,
frm_len, freq,
rx_rssi);
if (!processed) {
hdd_err("Unable to process rx public action frame");
return;
}
}
if (is_tdls_action_frame(pb_frames, frm_len))
log_rx_tdls_action_frame(pb_frames, frm_len);
if (is_qos_action_frame(pb_frames, frm_len))
sme_update_dsc_pto_up_mapping(hdd_ctx->hHal,
adapter->hddWmmDscpToUpMap,
adapter->sessionId);
indicate:
hdd_debug("Indicate Frame over NL80211 sessionid : %d, idx :%d",
adapter->sessionId, adapter->dev->ifindex);
indicate_rx_mgmt_over_nl80211(adapter, frm_len, pb_frames,
freq, rx_rssi);
}