blob: 53ae33f3bb8c046e5ab636c2319cee6d4c1de47a [file] [log] [blame]
/*
* Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**========================================================================
\file wlan_hdd_tdls.c
\brief WLAN Host Device Driver implementation for TDLS
========================================================================*/
#include <wlan_hdd_includes.h>
#include <aniGlobal.h>
#include <wlan_hdd_hostapd.h>
#include <net/cfg80211.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/list.h>
#include <linux/etherdevice.h>
#include <net/ieee80211_radiotap.h>
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_assoc.h"
#include "sme_Api.h"
#include "vos_sched.h"
/**
* wlan_hdd_tdls_determine_channel_opclass() - determine channel and opclass
* @hddctx: pointer to hdd context
* @adapter: pointer to adapter
* @curr_peer: pointer to current tdls peer
* @channel: pointer to channel
* @opclass: pointer to opclass
*
* Function determines the channel and operating class
*
* Return: None
*/
static void wlan_hdd_tdls_determine_channel_opclass(hdd_context_t *hddctx,
hdd_adapter_t *adapter, hddTdlsPeer_t *curr_peer,
uint32_t *channel, uint32_t *opclass)
{
hdd_station_ctx_t *hdd_sta_ctx;
/*
* If tdls offchannel is not enabled then we provide base channel
* and in that case pass opclass as 0 since opclass is mainly needed
* for offchannel cases.
*/
if (!(hddctx->cfg_ini->fEnableTDLSOffChannel) ||
(hddctx->tdls_fw_off_chan_mode != ENABLE_CHANSWITCH)) {
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
*channel = hdd_sta_ctx->conn_info.operationChannel;
*opclass = 0;
} else {
*channel = curr_peer->pref_off_chan_num;
*opclass = curr_peer->op_class_for_pref_off_chan;
}
}
static u8 wlan_hdd_tdls_hash_key (const u8 *mac)
{
int i;
u8 key = 0;
for (i = 0; i < 6; i++)
key ^= mac[i];
return key;
}
#ifdef FEATURE_WLAN_DIAG_SUPPORT
/**
* hdd_send_wlan_tdls_teardown_event()- send TDLS teardown event
* @reason: reason for tear down.
* @peer_mac: peer mac
*
* This Function send TDLS teardown diag event
*
* Return: void.
*/
void hdd_send_wlan_tdls_teardown_event(uint32_t reason,
uint8_t *peer_mac)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_tear_down,
struct vos_event_tdls_teardown);
vos_mem_zero(&tdls_tear_down,
sizeof(tdls_tear_down));
tdls_tear_down.reason = reason;
vos_mem_copy(tdls_tear_down.peer_mac, peer_mac, HDD_MAC_ADDR_LEN);
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_tear_down,
EVENT_WLAN_TDLS_TEARDOWN);
}
/**
* hdd_wlan_tdls_enable_link_event()- send TDLS enable link event
* @peer_mac: peer mac
* @is_off_chan_supported: Does peer supports off chan
* @is_off_chan_configured: If off channel is configured
* @is_off_chan_established: If off chan is established
*
* This Function send TDLS enable link diag event
*
* Return: void.
*/
void hdd_wlan_tdls_enable_link_event(const uint8_t *peer_mac,
uint8_t is_off_chan_supported,
uint8_t is_off_chan_configured,
uint8_t is_off_chan_established)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_event,
struct vos_event_tdls_enable_link);
vos_mem_zero(&tdls_event,
sizeof(tdls_event));
vos_mem_copy(tdls_event.peer_mac,
peer_mac, HDD_MAC_ADDR_LEN);
tdls_event.is_off_chan_supported =
is_off_chan_supported;
tdls_event.is_off_chan_configured =
is_off_chan_configured;
tdls_event.is_off_chan_established =
is_off_chan_established;
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_event,
EVENT_WLAN_TDLS_ENABLE_LINK);
}
/**
* hdd_wlan_block_scan_by_tdls_event()- send event
* if scan is blocked by tdls
*
* This Function send send diag event if scan is
* blocked by tdls
*
* Return: void.
*/
void hdd_wlan_block_scan_by_tdls_event(void)
{
WLAN_VOS_DIAG_EVENT_DEF(tdls_scan_block_status,
struct vos_event_tdls_scan_rejected);
vos_mem_zero(&tdls_scan_block_status, sizeof(tdls_scan_block_status));
tdls_scan_block_status.status = true;
WLAN_VOS_DIAG_EVENT_REPORT(&tdls_scan_block_status,
EVENT_TDLS_SCAN_BLOCK);
}
#endif
/**
* wlan_hdd_tdls_disable_offchan_and_teardown_links - Disable offchannel
* and teardown TDLS links
* @hddCtx : pointer to hdd context
*
* Return: None
*/
void wlan_hdd_tdls_disable_offchan_and_teardown_links(hdd_context_t *hddctx)
{
u16 connected_tdls_peers = 0;
hddTdlsPeer_t *curr_peer;
u8 staidx;
hdd_adapter_t *adapter = NULL;
if (eTDLS_SUPPORT_NOT_ENABLED == hddctx->tdls_mode) {
hddLog(LOG1, FL("TDLS mode is disabled OR not enabled in FW"));
return ;
}
adapter = hdd_get_adapter(hddctx, WLAN_HDD_INFRA_STATION);
if (adapter == NULL) {
hddLog(LOGE, FL("Station Adapter Not Found"));
return;
}
connected_tdls_peers = wlan_hdd_tdlsConnectedPeers(adapter);
if (!connected_tdls_peers) {
hddLog(LOG1, FL("No TDLS connected peers to delete"));
return ;
}
/* TDLS is not supported in case of concurrency.
* Disable TDLS Offchannel in FW to avoid more
* than two concurrent channels and generate TDLS
* teardown indication to supplicant.
* Below function Finds the first connected peer and
* disables TDLS offchannel for that peer.
* FW enables TDLS offchannel only when there is
* one TDLS peer. When there are more than one TDLS peer,
* there will not be TDLS offchannel in FW.
* So to avoid sending multiple request to FW, for now,
* just invoke offchannel mode functions only once
*/
hdd_set_tdls_offchannel(hddctx, hddctx->cfg_ini->fTDLSPrefOffChanNum);
hdd_set_tdls_secoffchanneloffset(hddctx,
TDLS_SEC_OFFCHAN_OFFSET_40PLUS);
hdd_set_tdls_offchannelmode(adapter, DISABLE_CHANSWITCH);
/* Send Msg to PE for deleting all the TDLS peers */
sme_delete_all_tdls_peers(hddctx->hHal, adapter->sessionId);
for (staidx = 0; staidx < hddctx->max_num_tdls_sta;
staidx++) {
if (!hddctx->tdlsConnInfo[staidx].staId)
continue;
curr_peer = wlan_hdd_tdls_find_all_peer(hddctx,
hddctx->tdlsConnInfo[staidx].peerMac.bytes);
if (!curr_peer)
continue;
hddLog(LOG1, FL("indicate TDLS teardown (staId %d)"),
curr_peer->staId);
/* Indicate teardown to supplicant */
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
/*
* Del Sta happened already as part of sme_delete_all_tdls_peers
* Hence clear hdd data structure.
*/
hdd_roamDeregisterTDLSSTA(adapter,
hddctx->tdlsConnInfo[staidx].staId);
wlan_hdd_tdls_decrement_peer_count(adapter);
wlan_hdd_tdls_reset_peer(adapter, curr_peer->peerMac);
hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_CONCURRENCY,
curr_peer->peerMac);
hddctx->tdlsConnInfo[staidx].staId = 0;
hddctx->tdlsConnInfo[staidx].sessionId = 255;
vos_mem_zero(&hddctx->tdlsConnInfo[staidx].peerMac,
sizeof(v_MACADDR_t));
}
wlan_hdd_tdls_check_bmps(adapter);
}
/**
* hdd_tdls_notify_mode_change - Notify mode change
* @adapter: pointer to hdd adapter
* @hddCtx : pointer to hdd context
*
* Return: None
*/
void hdd_tdls_notify_mode_change(hdd_adapter_t *adapter, hdd_context_t *hddctx)
{
wlan_hdd_tdls_disable_offchan_and_teardown_links(hddctx);
}
/* Caller has to take the lock before calling this function */
static tANI_S32 wlan_hdd_tdls_peer_reset_discovery_processed(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
tmp->discovery_processed = 0;
}
}
return 0;
}
static tANI_U32 wlan_hdd_tdls_discovery_sent_cnt(hdd_context_t *pHddCtx)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
VOS_STATUS status = 0;
tANI_U32 count = 0;
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
count = count + pHddTdlsCtx->discovery_sent_cnt;
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
return count;
}
static void wlan_hdd_tdls_check_power_save_prohibited(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = NULL;
hdd_context_t *pHddCtx = NULL;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddTdlsCtx) || (NULL == pHddCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
if ((0 == pHddCtx->connected_peer_count) &&
(0 == wlan_hdd_tdls_discovery_sent_cnt(pHddCtx)))
{
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pHddTdlsCtx->pAdapter),
pAdapter->sessionId, 0);
return;
}
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pHddTdlsCtx->pAdapter),
pAdapter->sessionId, 1);
return;
}
static v_VOID_t wlan_hdd_tdls_discovery_timeout_peer_cb(v_PVOID_t userData)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
ENTER();
pHddTdlsCtx = (tdlsCtx_t *)userData;
if ((NULL == pHddTdlsCtx) || (NULL == pHddTdlsCtx->pAdapter) )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx or pAdapter points to NULL"));
return;
}
if (WLAN_HDD_ADAPTER_MAGIC != pHddTdlsCtx->pAdapter->magic) {
hddLog(LOGE, FL("pAdapter has invalid magic"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX( pHddTdlsCtx->pAdapter );
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
mutex_lock(&pHddCtx->tdls_lock);
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
if (eTDLS_LINK_DISCOVERING == tmp->link_status)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR " to idle state", __func__,
MAC_ADDR_ARRAY(tmp->peerMac));
mutex_unlock(&pHddCtx->tdls_lock);
if (tmp->discovery_attempt ==
pHddTdlsCtx->threshold_config.discovery_tries_n) {
tmp->discovery_attempt = 0;
tmp->discovery_cycles_retry_cnt++;
tmp->last_discovery_req_cycle_ts = jiffies;
}
wlan_hdd_tdls_set_peer_link_status(tmp,
eTDLS_LINK_IDLE,
eTDLS_LINK_NOT_SUPPORTED,
TRUE);
mutex_lock(&pHddCtx->tdls_lock);
}
}
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pHddTdlsCtx->pAdapter);
EXIT();
return;
}
static void wlan_hdd_tdls_free_list(tdlsCtx_t *pHddTdlsCtx,
bool del_forced_peer)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++)
{
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
/* Don't delete TDLS forced peers during STA disconnection */
if (!del_forced_peer && tmp->isForcedPeer)
continue;
list_del(pos);
vos_mem_free(tmp);
tmp = NULL;
}
}
}
static void wlan_hdd_tdls_schedule_scan(struct work_struct *work)
{
tdls_scan_context_t *scan_ctx =
container_of(work, tdls_scan_context_t, tdls_scan_work.work);
if (NULL == scan_ctx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("scan_ctx is NULL"));
return;
}
if (unlikely(TDLS_CTX_MAGIC != scan_ctx->magic))
return;
scan_ctx->attempt++;
wlan_hdd_cfg80211_scan(scan_ctx->wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
scan_ctx->dev,
#endif
scan_ctx->scan_request);
}
/* stop all monitoring timers per Adapter */
static void wlan_hdd_tdls_monitor_timers_stop(tdlsCtx_t *pHddTdlsCtx)
{
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
/* stop all the tdls timers running */
static void wlan_hdd_tdls_timers_stop(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_monitor_timers_stop(pHddTdlsCtx);
}
int wlan_hdd_tdls_init(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
tdlsCtx_t *pHddTdlsCtx = NULL;
int i;
v_U8_t staIdx;
tdlsInfo_t *tInfo;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
if (NULL == pHddCtx)
return -1;
mutex_lock(&pHddCtx->tdls_lock);
/* QCA 2.0 Discrete ANDs feature capability in cfg_ini with that
* received from target, so cfg_ini gives combined intersected result
*/
if ((FALSE == pHddCtx->cfg_ini->fEnableTDLSSupport)
)
{
pHddCtx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s TDLS not enabled (%d) or FW doesn't support",
__func__, pHddCtx->cfg_ini->fEnableTDLSSupport);
return 0;
}
/* TDLS is supported only in STA / P2P Client modes,
* hence the check for TDLS support in a specific Device mode.
* Do not return a failure rather do not continue further
* with the initialization as tdls_init would be called
* during the open adapter for a p2p interface at which point
* the device mode would be a P2P_DEVICE. The point here is to
* continue initialization for STA / P2P Client modes.
* TDLS exit also check for the device mode for clean up hence
* there is no issue even if success is returned.
*/
if (0 == WLAN_HDD_IS_TDLS_SUPPORTED_ADAPTER(pAdapter))
{
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/* Check for the valid pHddTdlsCtx. If valid do not further
* allocate the memory, rather continue with the initialization.
* If tdls_initialization would get reinvoked without tdls_exit
* getting invoked (SSR) there is no point to further proceed
* with the memory allocations.
*/
if (NULL == pAdapter->sessionCtx.station.pHddTdlsCtx)
{
pHddTdlsCtx = vos_mem_malloc(sizeof(tdlsCtx_t));
if (NULL == pHddTdlsCtx) {
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s malloc failed!", __func__);
return -1;
}
/* initialize TDLS pAdater context */
vos_mem_zero(pHddTdlsCtx, sizeof(tdlsCtx_t));
vos_timer_init(&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
VOS_TIMER_TYPE_WAKE_APPS,
wlan_hdd_tdls_discovery_timeout_peer_cb,
pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = pHddTdlsCtx;
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++)
INIT_LIST_HEAD(&pHddTdlsCtx->peer_list[i]);
} else {
struct list_head *head, *pos, *q;
hddTdlsPeer_t *tmp = NULL;
pHddTdlsCtx = pAdapter->sessionCtx.station.pHddTdlsCtx;
/* stop all timers */
wlan_hdd_tdls_timers_stop(pHddTdlsCtx);
/* remove entries from peer list only if peer is not forced */
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe(pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
if (FALSE == tmp->isForcedPeer) {
list_del(pos);
vos_mem_free(tmp);
tmp = NULL;
} else {
tmp->link_status = eTDLS_LINK_IDLE;
tmp->reason = eTDLS_LINK_UNSPECIFIED;
tmp->staId = 0;
tmp->discovery_attempt = 0;
tmp->discovery_cycles_retry_cnt = 0;
}
}
}
/* reset tdls peer count to 0 */
pHddCtx->connected_peer_count = 0;
}
/* initialize TDLS global context */
pHddCtx->connected_peer_count = 0;
pHddCtx->tdls_nss_switch_in_progress = false;
pHddCtx->tdls_teardown_peers_cnt = 0;
pHddCtx->tdls_nss_teardown_complete = false;
pHddCtx->tdls_nss_transition_mode = TDLS_NSS_TRANSITION_UNKNOWN;
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, 0);
pHddCtx->tdls_scan_ctxt.magic = 0;
pHddCtx->tdls_scan_ctxt.attempt = 0;
pHddCtx->tdls_scan_ctxt.reject = 0;
pHddCtx->tdls_scan_ctxt.scan_request = NULL;
if (pHddCtx->cfg_ini->fEnableTDLSSleepSta ||
pHddCtx->cfg_ini->fEnableTDLSBufferSta ||
pHddCtx->cfg_ini->fEnableTDLSOffChannel)
pHddCtx->max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA_P_UAPSD_OFFCHAN;
else
pHddCtx->max_num_tdls_sta = HDD_MAX_NUM_TDLS_STA;
hddLog(VOS_TRACE_LEVEL_INFO_HIGH, FL("max_num_tdls_sta: %d"),
pHddCtx->max_num_tdls_sta);
for (staIdx = 0; staIdx < pHddCtx->max_num_tdls_sta; staIdx++)
{
pHddCtx->tdlsConnInfo[staIdx].staId = 0;
pHddCtx->tdlsConnInfo[staIdx].sessionId = 255;
vos_mem_zero(&pHddCtx->tdlsConnInfo[staIdx].peerMac,
sizeof(v_MACADDR_t)) ;
}
pHddTdlsCtx->pAdapter = pAdapter;
pHddTdlsCtx->curr_candidate = NULL;
pHddTdlsCtx->magic = 0;
/* remember configuration even if it is not used right now. it could be used later */
pHddTdlsCtx->threshold_config.tx_period_t = pHddCtx->cfg_ini->fTDLSTxStatsPeriod;
pHddTdlsCtx->threshold_config.tx_packet_n = pHddCtx->cfg_ini->fTDLSTxPacketThreshold;
pHddTdlsCtx->threshold_config.discovery_period_t = pHddCtx->cfg_ini->fTDLSDiscoveryPeriod;
pHddTdlsCtx->threshold_config.discovery_tries_n = pHddCtx->cfg_ini->fTDLSMaxDiscoveryAttempt;
pHddTdlsCtx->threshold_config.idle_timeout_t = pHddCtx->cfg_ini->fTDLSIdleTimeout;
pHddTdlsCtx->threshold_config.idle_packet_n = pHddCtx->cfg_ini->fTDLSIdlePacketThreshold;
pHddTdlsCtx->threshold_config.rssi_hysteresis = pHddCtx->cfg_ini->fTDLSRSSIHysteresis;
pHddTdlsCtx->threshold_config.rssi_trigger_threshold = pHddCtx->cfg_ini->fTDLSRSSITriggerThreshold;
pHddTdlsCtx->threshold_config.rssi_teardown_threshold = pHddCtx->cfg_ini->fTDLSRSSITeardownThreshold;
pHddTdlsCtx->threshold_config.rssi_delta = pHddCtx->cfg_ini->fTDLSRSSIDelta;
if (FALSE == pHddCtx->cfg_ini->fEnableTDLSImplicitTrigger)
{
pHddCtx->tdls_mode = eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY;
hddLog(VOS_TRACE_LEVEL_INFO, "%s TDLS Implicit trigger not enabled!", __func__);
} else if (TRUE == pHddCtx->cfg_ini->fTDLSExternalControl) {
pHddCtx->tdls_mode = eTDLS_SUPPORT_EXTERNAL_CONTROL;
} else {
pHddCtx->tdls_mode = eTDLS_SUPPORT_ENABLED;
}
vos_init_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work,
wlan_hdd_tdls_schedule_scan);
/*
* Release tdls lock before calling in SME api
* which would try to acquire sme lock.
*/
mutex_unlock(&pHddCtx->tdls_lock);
tInfo = vos_mem_malloc(sizeof(tdlsInfo_t));
if (NULL == tInfo)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: vos_mem_alloc failed for tInfo", __func__);
vos_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
vos_mem_free(pHddTdlsCtx);
return -1;
}
tInfo->vdev_id = pAdapter->sessionId;
tInfo->tdls_state = pHddCtx->tdls_mode;
tInfo->notification_interval_ms = pHddTdlsCtx->threshold_config.tx_period_t;
tInfo->tx_discovery_threshold = pHddTdlsCtx->threshold_config.tx_packet_n;
tInfo->tx_teardown_threshold = pHddTdlsCtx->threshold_config.idle_packet_n;
tInfo->rssi_teardown_threshold =
pHddTdlsCtx->threshold_config.rssi_teardown_threshold;
tInfo->rssi_delta = pHddTdlsCtx->threshold_config.rssi_delta;
tInfo->tdls_options = 0;
if (pHddCtx->cfg_ini->fEnableTDLSOffChannel) {
tInfo->tdls_options |= ENA_TDLS_OFFCHAN;
pHddCtx->tdls_fw_off_chan_mode = ENABLE_CHANSWITCH;
}
if (pHddCtx->cfg_ini->fEnableTDLSBufferSta)
tInfo->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->cfg_ini->fEnableTDLSSleepSta)
tInfo->tdls_options |= ENA_TDLS_SLEEP_STA;
tInfo->peer_traffic_ind_window =
pHddCtx->cfg_ini->fTDLSPuapsdPTIWindow;
tInfo->peer_traffic_response_timeout =
pHddCtx->cfg_ini->fTDLSPuapsdPTRTimeout;
tInfo->puapsd_mask =
pHddCtx->cfg_ini->fTDLSUapsdMask;
tInfo->puapsd_inactivity_time =
pHddCtx->cfg_ini->fTDLSPuapsdInactivityTimer;
tInfo->puapsd_rx_frame_threshold =
pHddCtx->cfg_ini->fTDLSRxFrameThreshold;
tInfo->teardown_notification_ms =
pHddCtx->cfg_ini->fTDLSIdleTimeout;
tInfo->tdls_peer_kickout_threshold =
pHddCtx->cfg_ini->tdls_peer_kickout_threshold;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d, "
"teardown_notification_ms: %d, "
"tdls_peer_kickout_threshold: %d ",
__func__,
tInfo->vdev_id,
tInfo->tdls_state,
tInfo->notification_interval_ms,
tInfo->tx_discovery_threshold,
tInfo->tx_teardown_threshold,
tInfo->rssi_teardown_threshold,
tInfo->rssi_delta,
tInfo->tdls_options,
tInfo->peer_traffic_ind_window,
tInfo->peer_traffic_response_timeout,
tInfo->puapsd_mask,
tInfo->puapsd_inactivity_time,
tInfo->puapsd_rx_frame_threshold,
tInfo->teardown_notification_ms,
tInfo->tdls_peer_kickout_threshold);
halStatus = sme_UpdateFwTdlsState(pHddCtx->hHal, tInfo, TRUE);
if (eHAL_STATUS_SUCCESS != halStatus)
{
vos_mem_free(tInfo);
vos_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
vos_mem_free(pHddTdlsCtx);
return -1;
}
return 0;
}
static void wlan_hdd_tdls_monitor_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
vos_timer_destroy(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
/* destroy all the tdls timers running */
static void wlan_hdd_tdls_timers_destroy(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_monitor_timers_destroy(pHddTdlsCtx);
}
static void wlan_hdd_tdls_free_scan_request(tdls_scan_context_t *tdls_scan_ctx)
{
if (NULL == tdls_scan_ctx) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("tdls_scan_ctx is NULL"));
return;
}
tdls_scan_ctx->attempt = 0;
tdls_scan_ctx->reject = 0;
tdls_scan_ctx->magic = 0;
tdls_scan_ctx->scan_request = NULL;
return;
}
void wlan_hdd_tdls_exit(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
tdlsInfo_t *tInfo;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
if (!pHddCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("pHddCtx is NULL"));
return;
}
if (!test_bit(TDLS_INIT_DONE, &pAdapter->event_flags)) {
hddLog(LOG1, FL("TDLS init was not done, exit"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
/* TDLS context can be null and might have been freed up during
* cleanup for STA adapter
*/
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOG2, FL("pHddTdlsCtx is NULL, adapter device mode %s(%d)"),
hdd_device_mode_to_string(pAdapter->device_mode),
pAdapter->device_mode);
goto done;
}
vos_flush_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work);
/* must stop timer here before freeing peer list, because peerIdleTimer is
part of peer list structure. */
wlan_hdd_tdls_timers_destroy(pHddTdlsCtx);
wlan_hdd_tdls_free_list(pHddTdlsCtx, true);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_free_scan_request(&pHddCtx->tdls_scan_ctxt);
/* No need to post message during driver unload because MC thread is
already shutdown */
if ( !pHddCtx->isUnloadInProgress)
{
tInfo = vos_mem_malloc(sizeof(tdlsInfo_t));
if (NULL != tInfo)
{
tInfo->vdev_id = pAdapter->sessionId;
tInfo->tdls_state = eTDLS_SUPPORT_DISABLED;
mutex_lock(&pHddCtx->tdls_lock);
tInfo->notification_interval_ms =
pHddTdlsCtx->threshold_config.tx_period_t;
tInfo->tx_discovery_threshold =
pHddTdlsCtx->threshold_config.tx_packet_n;
tInfo->tx_teardown_threshold =
pHddTdlsCtx->threshold_config.idle_packet_n;
tInfo->rssi_teardown_threshold =
pHddTdlsCtx->threshold_config.rssi_teardown_threshold;
tInfo->rssi_delta = pHddTdlsCtx->threshold_config.rssi_delta;
mutex_unlock(&pHddCtx->tdls_lock);
tInfo->tdls_options = 0;
if (pHddCtx->cfg_ini->fEnableTDLSOffChannel)
tInfo->tdls_options |= ENA_TDLS_OFFCHAN;
if (pHddCtx->cfg_ini->fEnableTDLSBufferSta)
tInfo->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->cfg_ini->fEnableTDLSSleepSta)
tInfo->tdls_options |= ENA_TDLS_SLEEP_STA;
tInfo->peer_traffic_ind_window =
pHddCtx->cfg_ini->fTDLSPuapsdPTIWindow;
tInfo->peer_traffic_response_timeout =
pHddCtx->cfg_ini->fTDLSPuapsdPTRTimeout;
tInfo->puapsd_mask =
pHddCtx->cfg_ini->fTDLSUapsdMask;
tInfo->puapsd_inactivity_time =
pHddCtx->cfg_ini->fTDLSPuapsdInactivityTimer;
tInfo->puapsd_rx_frame_threshold =
pHddCtx->cfg_ini->fTDLSRxFrameThreshold;
tInfo->teardown_notification_ms =
pHddCtx->cfg_ini->fTDLSIdleTimeout;
tInfo->tdls_peer_kickout_threshold =
pHddCtx->cfg_ini->tdls_peer_kickout_threshold;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d, "
"teardown_notification_ms: %d, "
"tdls_peer_kickout_threshold: %d ",
__func__,
tInfo->vdev_id,
tInfo->tdls_state,
tInfo->notification_interval_ms,
tInfo->tx_discovery_threshold,
tInfo->tx_teardown_threshold,
tInfo->rssi_teardown_threshold,
tInfo->rssi_delta,
tInfo->tdls_options,
tInfo->peer_traffic_ind_window,
tInfo->peer_traffic_response_timeout,
tInfo->puapsd_mask,
tInfo->puapsd_inactivity_time,
tInfo->puapsd_rx_frame_threshold,
tInfo->teardown_notification_ms,
tInfo->tdls_peer_kickout_threshold);
halStatus = sme_UpdateFwTdlsState(pHddCtx->hHal, tInfo, FALSE);
if (eHAL_STATUS_SUCCESS != halStatus)
{
vos_mem_free(tInfo);
}
}
else
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: vos_mem_alloc failed for tInfo", __func__);
}
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx->magic = 0;
pHddTdlsCtx->pAdapter = NULL;
vos_mem_free(pHddTdlsCtx);
pAdapter->sessionCtx.station.pHddTdlsCtx = NULL;
pHddTdlsCtx = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
done:
clear_bit(TDLS_INIT_DONE, &pAdapter->event_flags);
}
/* if mac address exist, return pointer
if mac address doesn't exist, create a list and add, return pointer
return NULL if fails to get new mac address
*/
hddTdlsPeer_t *wlan_hdd_tdls_get_peer(hdd_adapter_t *pAdapter, const u8 *mac,
bool need_lock)
{
struct list_head *head;
hddTdlsPeer_t *peer;
u8 key;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return NULL;
if (need_lock)
mutex_lock(&pHddCtx->tdls_lock);
/* if already there, just update */
peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (peer != NULL)
{
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
return peer;
}
/* not found, allocate and add the list */
peer = vos_mem_malloc(sizeof(hddTdlsPeer_t));
if (NULL == peer) {
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_ERROR, "%s peer malloc failed!", __func__);
return NULL;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
vos_mem_free(peer);
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(LOG1, FL("pHddTdlsCtx is NULL"));
return NULL;
}
key = wlan_hdd_tdls_hash_key(mac);
head = &pHddTdlsCtx->peer_list[key];
vos_mem_zero(peer, sizeof(hddTdlsPeer_t));
vos_mem_copy(peer->peerMac, mac, sizeof(peer->peerMac));
peer->pHddTdlsCtx = pHddTdlsCtx;
peer->pref_off_chan_num = pHddCtx->cfg_ini->fTDLSPrefOffChanNum;
peer->op_class_for_pref_off_chan =
wlan_hdd_find_opclass(pHddCtx->hHal, peer->pref_off_chan_num,
pHddCtx->cfg_ini->fTDLSPrefOffChanBandwidth);
list_add_tail(&peer->node, head);
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
return peer;
}
int wlan_hdd_tdls_set_cap(hdd_adapter_t *pAdapter,
const u8* mac,
tTDLSCapType cap)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx;
int status = 0;
hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer->tdls_support = cap;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
void wlan_hdd_tdls_set_peer_link_status(hddTdlsPeer_t *curr_peer,
tTDLSLinkStatus status,
tTDLSLinkReason reason,
bool need_lock)
{
tANI_U32 state = 0;
tANI_S32 res = 0;
hdd_context_t *pHddCtx;
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
if (curr_peer->pHddTdlsCtx == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer->pHddTdlsCtx is NULL"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(curr_peer->pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
hddLog(VOS_TRACE_LEVEL_WARN, "tdls set peer " MAC_ADDRESS_STR " link status to %u",
MAC_ADDR_ARRAY(curr_peer->peerMac), status);
if (need_lock)
mutex_lock(&pHddCtx->tdls_lock);
curr_peer->link_status = status;
/* If TDLS link status is already passed the discovery state
* then clear discovery attempt count
*/
if (status >= eTDLS_LINK_DISCOVERED)
{
curr_peer->discovery_attempt = 0;
curr_peer->discovery_cycles_retry_cnt = 0;
}
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
{
uint32_t opclass;
uint32_t channel;
hdd_adapter_t *adapter = curr_peer->pHddTdlsCtx->pAdapter;
/*save the reason for any further query*/
curr_peer->reason = reason;
wlan_hdd_tdls_determine_channel_opclass(pHddCtx, adapter, curr_peer,
&channel, &opclass);
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
(*curr_peer->state_change_notification)(curr_peer->peerMac, opclass,
channel, state, res,
adapter);
}
return;
}
void wlan_hdd_tdls_set_link_status(hdd_adapter_t *pAdapter,
const u8* mac,
tTDLSLinkStatus linkStatus,
tTDLSLinkReason reason)
{
tANI_U32 state = 0;
tANI_S32 res = 0;
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, TRUE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
hddLog(LOG1, FL("tdls set peer " MAC_ADDRESS_STR " link status to %u"),
MAC_ADDR_ARRAY(curr_peer->peerMac), linkStatus);
curr_peer->link_status= linkStatus;
/* If TDLS link status is already passed the discovery state
* then clear discovery attempt count
*/
if (linkStatus >= eTDLS_LINK_DISCOVERED)
{
curr_peer->discovery_attempt = 0;
curr_peer->discovery_cycles_retry_cnt = 0;
}
mutex_unlock(&pHddCtx->tdls_lock);
if (curr_peer->isForcedPeer && curr_peer->state_change_notification)
{
uint32_t opclass;
uint32_t channel;
hdd_adapter_t *adapter = curr_peer->pHddTdlsCtx->pAdapter;
/*save the reason for any further query*/
curr_peer->reason = reason;
wlan_hdd_tdls_determine_channel_opclass(pHddCtx, adapter, curr_peer,
&channel, &opclass);
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, &state, &res);
(curr_peer->state_change_notification)(mac, opclass, channel, state,
res, adapter);
}
return;
}
int wlan_hdd_tdls_recv_discovery_resp(hdd_adapter_t *pAdapter, u8 *mac)
{
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
int status = 0;
ENTER();
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if ( NULL == pHddTdlsCtx )
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddTdlsCtx is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (NULL == curr_peer)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
if (pHddTdlsCtx->discovery_sent_cnt)
pHddTdlsCtx->discovery_sent_cnt--;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
if (0 == pHddTdlsCtx->discovery_sent_cnt)
{
vos_timer_stop(&pHddTdlsCtx->peerDiscoveryTimeoutTimer);
}
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Discovery(%u) Response from " MAC_ADDRESS_STR " link_status %d",
pHddTdlsCtx->discovery_sent_cnt, MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->link_status);
if (eTDLS_LINK_DISCOVERING == curr_peer->link_status)
{
/*
* Since we are here, it means Throughput threshold is already met.
* Make sure RSSI threshold is also met before setting up TDLS link
*/
if ((tANI_S32) curr_peer->rssi > (tANI_S32) pHddTdlsCtx->threshold_config.rssi_trigger_threshold)
{
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERED,
eTDLS_LINK_SUCCESS,
FALSE);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rssi Threshold met: "MAC_ADDRESS_STR" rssi = %d threshold= %d" ,
MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
pHddTdlsCtx->threshold_config.rssi_trigger_threshold);
cfg80211_tdls_oper_request(pAdapter->dev, curr_peer->peerMac, NL80211_TDLS_SETUP, FALSE, GFP_KERNEL);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"Rssi Threshold not met: "MAC_ADDRESS_STR" rssi = %d threshold = %d ",
MAC_ADDR_ARRAY(curr_peer->peerMac), curr_peer->rssi,
pHddTdlsCtx->threshold_config.rssi_trigger_threshold);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED,
FALSE);
/* if RSSI threshold is not met then allow further discovery
* attempts by decrementing count for the last attempt
*/
if (curr_peer->discovery_attempt)
curr_peer->discovery_attempt--;
}
}
else
{
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pAdapter);
goto ret_status;
}
curr_peer->tdls_support = eTDLS_CAP_SUPPORTED;
rel_lock:
mutex_unlock(&pHddCtx->tdls_lock);
ret_status:
EXIT();
return status;
}
int wlan_hdd_tdls_set_peer_caps(hdd_adapter_t *pAdapter,
const u8 *mac,
tCsrStaParams *StaParams,
tANI_BOOLEAN isBufSta,
tANI_BOOLEAN isOffChannelSupported,
bool is_qos_wmm_sta)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer->uapsdQueues = StaParams->uapsd_queues;
curr_peer->maxSp = StaParams->max_sp;
curr_peer->isBufSta = isBufSta;
curr_peer->isOffChannelSupported = isOffChannelSupported;
vos_mem_copy(curr_peer->supported_channels,
StaParams->supported_channels,
StaParams->supported_channels_len);
curr_peer->supported_channels_len =
StaParams->supported_channels_len;
vos_mem_copy(curr_peer->supported_oper_classes,
StaParams->supported_oper_classes,
StaParams->supported_oper_classes_len);
curr_peer->supported_oper_classes_len =
StaParams->supported_oper_classes_len;
curr_peer->qos = is_qos_wmm_sta;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
int wlan_hdd_tdls_get_link_establish_params(hdd_adapter_t *pAdapter,
const u8 *mac,
tCsrTdlsLinkEstablishParams* tdlsLinkEstablishParams)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
tdlsLinkEstablishParams->isResponder = curr_peer->is_responder;
tdlsLinkEstablishParams->uapsdQueues = curr_peer->uapsdQueues;
tdlsLinkEstablishParams->maxSp = curr_peer->maxSp;
tdlsLinkEstablishParams->isBufSta = curr_peer->isBufSta;
tdlsLinkEstablishParams->isOffChannelSupported =
curr_peer->isOffChannelSupported;
vos_mem_copy(tdlsLinkEstablishParams->supportedChannels,
curr_peer->supported_channels,
curr_peer->supported_channels_len);
tdlsLinkEstablishParams->supportedChannelsLen =
curr_peer->supported_channels_len;
vos_mem_copy(tdlsLinkEstablishParams->supportedOperClasses,
curr_peer->supported_oper_classes,
curr_peer->supported_oper_classes_len);
tdlsLinkEstablishParams->supportedOperClassesLen =
curr_peer->supported_oper_classes_len;
tdlsLinkEstablishParams->qos = curr_peer->qos;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
int wlan_hdd_tdls_set_rssi(hdd_adapter_t *pAdapter, const u8 *mac,
tANI_S8 rxRssi)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(hdd_ctx)))
return -1;
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
mutex_unlock(&hdd_ctx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return -1;
}
curr_peer->rssi = rxRssi;
mutex_unlock(&hdd_ctx->tdls_lock);
return 0;
}
int wlan_hdd_tdls_set_responder(hdd_adapter_t *pAdapter, const u8 *mac,
tANI_U8 responder)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer->is_responder = responder;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
int wlan_hdd_tdls_set_signature(hdd_adapter_t *pAdapter, const u8 *mac,
tANI_U8 uSignature)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer->signature = uSignature;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
void wlan_hdd_tdls_extract_da(struct sk_buff *skb, u8 *mac)
{
memcpy(mac, skb->data, 6);
}
void wlan_hdd_tdls_extract_sa(struct sk_buff *skb, u8 *mac)
{
memcpy(mac, skb->data+6, 6);
}
int wlan_hdd_tdls_increment_pkt_count(hdd_adapter_t *pAdapter, const u8 *mac,
u8 tx)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
if (eTDLS_SUPPORT_ENABLED != hdd_ctx->tdls_mode &&
eTDLS_SUPPORT_EXTERNAL_CONTROL != hdd_ctx->tdls_mode) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL) {
hddLog(LOG1, FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
if (tx)
curr_peer->tx_pkt++;
else
curr_peer->rx_pkt++;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
static int wlan_hdd_tdls_check_config(tdls_config_params_t *config)
{
if (config->tdls > 2)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 1st argument %d. <0...2>",
__func__, config->tdls);
return -1;
}
if (config->tx_period_t < CFG_TDLS_TX_STATS_PERIOD_MIN ||
config->tx_period_t > CFG_TDLS_TX_STATS_PERIOD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 2nd argument %d. <%d...%ld> \
resetting to default value %d",
__func__,
config->tx_period_t,
CFG_TDLS_TX_STATS_PERIOD_MIN,
CFG_TDLS_TX_STATS_PERIOD_MAX,
CFG_TDLS_TX_STATS_PERIOD_DEFAULT);
config->tx_period_t = CFG_TDLS_TX_STATS_PERIOD_DEFAULT;
}
if (config->tx_packet_n < CFG_TDLS_TX_PACKET_THRESHOLD_MIN ||
config->tx_packet_n > CFG_TDLS_TX_PACKET_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 3rd argument %d. <%d...%ld> \
resetting to default value %d",
__func__,
config->tx_packet_n,
CFG_TDLS_TX_PACKET_THRESHOLD_MIN,
CFG_TDLS_TX_PACKET_THRESHOLD_MAX,
CFG_TDLS_TX_PACKET_THRESHOLD_DEFAULT);
config->tx_packet_n = CFG_TDLS_TX_PACKET_THRESHOLD_DEFAULT;
}
if (config->discovery_period_t < CFG_TDLS_DISCOVERY_PERIOD_MIN ||
config->discovery_period_t > CFG_TDLS_DISCOVERY_PERIOD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 4th argument %d. <%d...%ld> \
resetting to default value %d",
__func__,
config->discovery_period_t,
CFG_TDLS_DISCOVERY_PERIOD_MIN,
CFG_TDLS_DISCOVERY_PERIOD_MAX,
CFG_TDLS_DISCOVERY_PERIOD_DEFAULT);
config->discovery_period_t = CFG_TDLS_DISCOVERY_PERIOD_DEFAULT;
}
if (config->discovery_tries_n < CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN ||
config->discovery_tries_n > CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 5th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->discovery_tries_n,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MIN,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_MAX,
CFG_TDLS_MAX_DISCOVERY_ATTEMPT_DEFAULT);
config->discovery_tries_n = CFG_TDLS_MAX_DISCOVERY_ATTEMPT_DEFAULT;
}
if (config->idle_timeout_t < CFG_TDLS_IDLE_TIMEOUT_MIN ||
config->idle_timeout_t > CFG_TDLS_IDLE_TIMEOUT_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 6th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->idle_timeout_t,
CFG_TDLS_IDLE_TIMEOUT_MIN,
CFG_TDLS_IDLE_TIMEOUT_MAX,
CFG_TDLS_IDLE_TIMEOUT_DEFAULT);
config->idle_timeout_t = CFG_TDLS_IDLE_TIMEOUT_DEFAULT;
}
if (config->idle_packet_n < CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN ||
config->idle_packet_n > CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 7th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->idle_packet_n,
CFG_TDLS_IDLE_PACKET_THRESHOLD_MIN,
CFG_TDLS_IDLE_PACKET_THRESHOLD_MAX,
CFG_TDLS_IDLE_PACKET_THRESHOLD_DEFAULT);
config->idle_packet_n = CFG_TDLS_IDLE_PACKET_THRESHOLD_DEFAULT;
}
if (config->rssi_hysteresis < CFG_TDLS_RSSI_HYSTERESIS_MIN ||
config->rssi_hysteresis > CFG_TDLS_RSSI_HYSTERESIS_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 8th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->rssi_hysteresis,
CFG_TDLS_RSSI_HYSTERESIS_MIN,
CFG_TDLS_RSSI_HYSTERESIS_MAX,
CFG_TDLS_RSSI_HYSTERESIS_DEFAULT);
config->rssi_hysteresis = CFG_TDLS_RSSI_HYSTERESIS_DEFAULT;
}
if (config->rssi_trigger_threshold < CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN ||
config->rssi_trigger_threshold > CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 9th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->rssi_trigger_threshold,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MIN,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_MAX,
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_DEFAULT);
config->rssi_trigger_threshold =
CFG_TDLS_RSSI_TRIGGER_THRESHOLD_DEFAULT;
}
if (config->rssi_teardown_threshold < CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN ||
config->rssi_teardown_threshold > CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s invalid 10th argument %d. <%d...%d> \
resetting to default value %d",
__func__,
config->rssi_teardown_threshold,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MIN,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_MAX,
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_DEFAULT);
config->rssi_teardown_threshold =
CFG_TDLS_RSSI_TEARDOWN_THRESHOLD_DEFAULT;
}
return 0;
}
/* Caller has to take the lock before calling this function */
static void wlan_tdd_tdls_reset_tx_rx(tdlsCtx_t *pHddTdlsCtx)
{
int i;
struct list_head *head;
hddTdlsPeer_t *tmp;
struct list_head *pos, *q;
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each_safe (pos, q, head) {
tmp = list_entry(pos, hddTdlsPeer_t, node);
tmp->tx_pkt = 0;
tmp->rx_pkt = 0;
}
}
return;
}
static void wlan_hdd_tdls_implicit_disable(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_timers_stop(pHddTdlsCtx);
}
static void wlan_hdd_tdls_implicit_enable(tdlsCtx_t *pHddTdlsCtx)
{
wlan_hdd_tdls_peer_reset_discovery_processed(pHddTdlsCtx);
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_tdd_tdls_reset_tx_rx(pHddTdlsCtx);
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
}
static void wlan_hdd_tdls_set_mode(hdd_context_t *pHddCtx,
eTDLSSupportMode tdls_mode,
v_BOOL_t bUpdateLast,
enum tdls_disable_source source)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
VOS_STATUS status;
hdd_adapter_t *pAdapter;
tdlsCtx_t *pHddTdlsCtx;
ENTER();
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s mode %d", __func__, (int)tdls_mode);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->tdls_mode == tdls_mode)
{
mutex_unlock(&pHddCtx->tdls_lock);
hddLog(VOS_TRACE_LEVEL_INFO,
"%s already in mode %d", __func__, (int)tdls_mode);
/* TDLS is already disabled hence set source mask and return */
if (eTDLS_SUPPORT_DISABLED == tdls_mode) {
set_bit((unsigned long)source, &pHddCtx->tdls_source_bitmap);
return;
}
/* TDLS is already enabled hence clear source mask and return */
if (eTDLS_SUPPORT_ENABLED == tdls_mode) {
clear_bit((unsigned long)source,
&pHddCtx->tdls_source_bitmap);
return;
}
return;
}
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
if(eTDLS_SUPPORT_ENABLED == tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == tdls_mode) {
clear_bit((unsigned long)source,
&pHddCtx->tdls_source_bitmap);
/*
* Check if any TDLS source bit is set and if bitmap is
* not zero then we should not enable TDLS
*/
if (pHddCtx->tdls_source_bitmap) {
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
wlan_hdd_tdls_implicit_enable(pHddTdlsCtx);
} else if (eTDLS_SUPPORT_DISABLED == tdls_mode) {
set_bit((unsigned long)source,
&pHddCtx->tdls_source_bitmap);
wlan_hdd_tdls_implicit_disable(pHddTdlsCtx);
if (pHddTdlsCtx->is_tdls_disabled_bmps) {
if (FALSE == sme_IsPmcBmps(pHddCtx->hHal)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
"%s: TDLS is disabled. Enable BMPS", __func__);
status = hdd_enable_bmps_imps(pHddCtx);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = false;
}
}
} else if (eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == tdls_mode) {
clear_bit((unsigned long)source,
&pHddCtx->tdls_source_bitmap);
wlan_hdd_tdls_implicit_disable(pHddTdlsCtx);
/*
* Check if any TDLS source bit is set and if bitmap is
* not zero then we should not enable TDLS
*/
if (pHddCtx->tdls_source_bitmap) {
mutex_unlock(&pHddCtx->tdls_lock);
return;
}
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
if(bUpdateLast)
{
pHddCtx->tdls_mode_last = tdls_mode;
}
else
{
pHddCtx->tdls_mode_last = pHddCtx->tdls_mode;
}
pHddCtx->tdls_mode = tdls_mode;
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
int wlan_hdd_tdls_set_params(struct net_device *dev, tdls_config_params_t *config)
{
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(dev);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
eTDLSSupportMode req_tdls_mode;
tdlsInfo_t *tdlsParams;
eHalStatus halStatus = eHAL_STATUS_FAILURE;
if (NULL == pHddTdlsCtx)
{
hddLog(VOS_TRACE_LEVEL_ERROR, FL("TDLS not enabled!"));
return -1;
}
if (wlan_hdd_tdls_check_config(config) != 0)
{
return -1;
}
/* config->tdls is mapped to 0->1, 1->2, 2->3 */
req_tdls_mode = config->tdls + 1;
if (pHddCtx->tdls_mode == req_tdls_mode)
{
hddLog(VOS_TRACE_LEVEL_ERROR, "%s already in mode %d", __func__, config->tdls);
return -1;
}
/* copy the configuration only when given tdls mode is implicit trigger enable */
if (eTDLS_SUPPORT_ENABLED == req_tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == req_tdls_mode)
{
memcpy(&pHddTdlsCtx->threshold_config, config, sizeof(tdls_config_params_t));
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"iw set tdls params: %d %d %d %d %d %d %d %d %d %d",
config->tdls,
config->tx_period_t,
config->tx_packet_n,
config->discovery_period_t,
config->discovery_tries_n,
config->idle_timeout_t,
config->idle_packet_n,
config->rssi_hysteresis,
config->rssi_trigger_threshold,
config->rssi_teardown_threshold);
wlan_hdd_tdls_set_mode(pHddCtx, req_tdls_mode, TRUE,
HDD_SET_TDLS_MODE_SOURCE_USER);
tdlsParams = vos_mem_malloc(sizeof(tdlsInfo_t));
if (NULL == tdlsParams)
{
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: vos_mem_alloc failed for tdlsParams", __func__);
return -1;
}
tdlsParams->vdev_id = pAdapter->sessionId;
tdlsParams->tdls_state = req_tdls_mode;
tdlsParams->notification_interval_ms = config->tx_period_t;
tdlsParams->tx_discovery_threshold = config->tx_packet_n;
tdlsParams->tx_teardown_threshold = config->idle_packet_n;
tdlsParams->rssi_teardown_threshold = config->rssi_teardown_threshold;
tdlsParams->rssi_delta = config->rssi_delta;
tdlsParams->tdls_options = 0;
if (pHddCtx->cfg_ini->fEnableTDLSOffChannel)
tdlsParams->tdls_options |= ENA_TDLS_OFFCHAN;
if (pHddCtx->cfg_ini->fEnableTDLSBufferSta)
tdlsParams->tdls_options |= ENA_TDLS_BUFFER_STA;
if (pHddCtx->cfg_ini->fEnableTDLSSleepSta)
tdlsParams->tdls_options |= ENA_TDLS_SLEEP_STA;
tdlsParams->peer_traffic_ind_window =
pHddCtx->cfg_ini->fTDLSPuapsdPTIWindow;
tdlsParams->peer_traffic_response_timeout =
pHddCtx->cfg_ini->fTDLSPuapsdPTRTimeout;
tdlsParams->puapsd_mask =
pHddCtx->cfg_ini->fTDLSUapsdMask;
tdlsParams->puapsd_inactivity_time =
pHddCtx->cfg_ini->fTDLSPuapsdInactivityTimer;
tdlsParams->puapsd_rx_frame_threshold =
pHddCtx->cfg_ini->fTDLSRxFrameThreshold;
tdlsParams->teardown_notification_ms =
pHddCtx->cfg_ini->fTDLSIdleTimeout;
tdlsParams->tdls_peer_kickout_threshold =
pHddCtx->cfg_ini->tdls_peer_kickout_threshold;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d, "
"teardown_notification_ms: %d, "
"tdls_peer_kickout_threshold: %d ",
__func__,
tdlsParams->vdev_id,
tdlsParams->tdls_state,
tdlsParams->notification_interval_ms,
tdlsParams->tx_discovery_threshold,
tdlsParams->tx_teardown_threshold,
tdlsParams->rssi_teardown_threshold,
tdlsParams->rssi_delta,
tdlsParams->tdls_options,
tdlsParams->peer_traffic_ind_window,
tdlsParams->peer_traffic_response_timeout,
tdlsParams->puapsd_mask,
tdlsParams->puapsd_inactivity_time,
tdlsParams->puapsd_rx_frame_threshold,
tdlsParams->teardown_notification_ms,
tdlsParams->tdls_peer_kickout_threshold);
halStatus = sme_UpdateFwTdlsState(pHddCtx->hHal, tdlsParams, TRUE);
if (eHAL_STATUS_SUCCESS != halStatus)
{
vos_mem_free(tdlsParams);
return -1;
}
return 0;
}
/**
* wlan_hdd_update_tdls_info - update tdls status info
* @adapter: ptr to device adapter.
* @tdls_prohibited: indicates whether tdls is prohibited.
* @tdls_chan_swit_prohibited: indicates whether tdls channel switch
* is prohibited.
*
* Normally an AP does not influence TDLS connection between STAs
* associated to it. But AP may set bits for TDLS Prohibited or
* TDLS Channel Switch Prohibited in Extended Capability IE in
* Assoc/Re-assoc response to STA. So after STA is connected to
* an AP, call this function to update TDLS status as per those
* bits set in Ext Cap IE in received Assoc/Re-assoc response
* from AP.
*
* Return: None.
*/
void wlan_hdd_update_tdls_info(hdd_adapter_t *adapter, bool tdls_prohibited,
bool tdls_chan_swit_prohibited)
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
tdlsCtx_t *hdd_tdls_ctx = WLAN_HDD_GET_TDLS_CTX_PTR(adapter);
tdlsInfo_t *tdls_param;
eHalStatus hal_status;
if (!hdd_tdls_ctx) {
/* may be TDLS is not applicable for this adapter */
hddLog(LOG1, FL("HDD TDLS context is null"));
return;
}
/* If TDLS support is disabled then no need to update target */
if (FALSE == hdd_ctx->cfg_ini->fEnableTDLSSupport) {
hddLog(LOG1, FL("TDLS not enabled"));
return;
}
/* If AP indicated TDLS Prohibited then disable tdls mode */
mutex_lock(&hdd_ctx->tdls_lock);
if (tdls_prohibited) {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_NOT_ENABLED;
} else {
if (FALSE == hdd_ctx->cfg_ini->fEnableTDLSImplicitTrigger) {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY;
} else if (TRUE == hdd_ctx->cfg_ini->fTDLSExternalControl) {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_EXTERNAL_CONTROL;
} else {
hdd_ctx->tdls_mode = eTDLS_SUPPORT_ENABLED;
}
}
mutex_unlock(&hdd_ctx->tdls_lock);
tdls_param = vos_mem_malloc(sizeof(*tdls_param));
if (!tdls_param) {
hddLog(LOGE,
FL("memory allocation failed for tdlsParams"));
return;
}
tdls_param->vdev_id = adapter->sessionId;
tdls_param->tdls_state = hdd_ctx->tdls_mode;
tdls_param->notification_interval_ms =
hdd_tdls_ctx->threshold_config.tx_period_t;
tdls_param->tx_discovery_threshold =
hdd_tdls_ctx->threshold_config.tx_packet_n;
tdls_param->tx_teardown_threshold =
hdd_tdls_ctx->threshold_config.idle_packet_n;
tdls_param->rssi_teardown_threshold =
hdd_tdls_ctx->threshold_config.rssi_teardown_threshold;
tdls_param->rssi_delta = hdd_tdls_ctx->threshold_config.rssi_delta;
tdls_param->tdls_options = 0;
/* Do not enable TDLS offchannel, if AP prohibited TDLS channel switch */
if ((hdd_ctx->cfg_ini->fEnableTDLSOffChannel) &&
(!tdls_chan_swit_prohibited)) {
tdls_param->tdls_options |= ENA_TDLS_OFFCHAN;
}
if (hdd_ctx->cfg_ini->fEnableTDLSBufferSta)
tdls_param->tdls_options |= ENA_TDLS_BUFFER_STA;
if (hdd_ctx->cfg_ini->fEnableTDLSSleepSta)
tdls_param->tdls_options |= ENA_TDLS_SLEEP_STA;
tdls_param->peer_traffic_ind_window =
hdd_ctx->cfg_ini->fTDLSPuapsdPTIWindow;
tdls_param->peer_traffic_response_timeout =
hdd_ctx->cfg_ini->fTDLSPuapsdPTRTimeout;
tdls_param->puapsd_mask =
hdd_ctx->cfg_ini->fTDLSUapsdMask;
tdls_param->puapsd_inactivity_time =
hdd_ctx->cfg_ini->fTDLSPuapsdInactivityTimer;
tdls_param->puapsd_rx_frame_threshold =
hdd_ctx->cfg_ini->fTDLSRxFrameThreshold;
tdls_param->teardown_notification_ms =
hdd_ctx->cfg_ini->fTDLSIdleTimeout;
tdls_param->tdls_peer_kickout_threshold =
hdd_ctx->cfg_ini->tdls_peer_kickout_threshold;
hddLog(LOG1,
FL("Setting tdls state and param in fw: "
"vdev_id: %d, "
"tdls_state: %d, "
"notification_interval_ms: %d, "
"tx_discovery_threshold: %d, "
"tx_teardown_threshold: %d, "
"rssi_teardown_threshold: %d, "
"rssi_delta: %d, "
"tdls_options: 0x%x, "
"peer_traffic_ind_window: %d, "
"peer_traffic_response_timeout: %d, "
"puapsd_mask: 0x%x, "
"puapsd_inactivity_time: %d, "
"puapsd_rx_frame_threshold: %d, "
"teardown_notification_ms: %d, "
"tdls_peer_kickout_threshold: %d "),
tdls_param->vdev_id,
tdls_param->tdls_state,
tdls_param->notification_interval_ms,
tdls_param->tx_discovery_threshold,
tdls_param->tx_teardown_threshold,
tdls_param->rssi_teardown_threshold,
tdls_param->rssi_delta,
tdls_param->tdls_options,
tdls_param->peer_traffic_ind_window,
tdls_param->peer_traffic_response_timeout,
tdls_param->puapsd_mask,
tdls_param->puapsd_inactivity_time,
tdls_param->puapsd_rx_frame_threshold,
tdls_param->teardown_notification_ms,
tdls_param->tdls_peer_kickout_threshold);
hal_status = sme_UpdateFwTdlsState(hdd_ctx->hHal, tdls_param, TRUE);
if (eHAL_STATUS_SUCCESS != hal_status) {
vos_mem_free(tdls_param);
return;
}
return;
}
int wlan_hdd_tdls_set_sta_id(hdd_adapter_t *pAdapter, const u8 *mac, u8 staId)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
int status = 0;
if (0 != (wlan_hdd_validate_context(hdd_ctx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&hdd_ctx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
curr_peer->staId = staId;
rel_lock:
mutex_unlock(&hdd_ctx->tdls_lock);
ret_status:
return status;
}
int wlan_hdd_tdls_set_extctrl_param(hdd_adapter_t *pAdapter, const uint8_t *mac,
uint32_t chan, uint32_t max_latency,
uint32_t op_class, uint32_t min_bandwidth)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
return -1;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->op_class_for_pref_off_chan = (uint8_t)op_class;
curr_peer->pref_off_chan_num = (uint8_t)chan;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/**
* wlan_hdd_tdls_update_peer_mac() - Update the peer mac information to firmware
* @pAdapter: hdd adapter to interface
* @mac: Mac address of the peer to be added
* @peerState: Current state of the peer
*
* This function updates TDLS peer state to firmware. Firmware will update
* connection table based on new peer state.
*
* Return:success (0) or failure (errno value)
*/
int wlan_hdd_tdls_update_peer_mac(hdd_adapter_t *pAdapter,
const uint8_t *mac,
uint32_t peerState)
{
tSmeTdlsPeerStateParams sme_tdls_peer_state_params;
eHalStatus hal_status = eHAL_STATUS_FAILURE;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
vos_mem_zero(&sme_tdls_peer_state_params,
sizeof(sme_tdls_peer_state_params));
sme_tdls_peer_state_params.vdevId = pAdapter->sessionId;
vos_mem_copy(&sme_tdls_peer_state_params.peerMacAddr,
mac, sizeof(sme_tdls_peer_state_params.peerMacAddr));
sme_tdls_peer_state_params.peerState = peerState;
hal_status = sme_UpdateTdlsPeerState(hdd_ctx->hHal,
&sme_tdls_peer_state_params);
if (eHAL_STATUS_SUCCESS != hal_status) {
hddLog(LOGE, FL("sme_UpdateTdlsPeerState failed for "
MAC_ADDRESS_STR), MAC_ADDR_ARRAY(mac));
return -EPERM;
}
return 0;
}
int wlan_hdd_tdls_set_force_peer(hdd_adapter_t *pAdapter, const u8 *mac,
tANI_BOOLEAN forcePeer)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (!pHddCtx)
return -1;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
mutex_unlock(&pHddCtx->tdls_lock);
return -1;
}
curr_peer->isForcedPeer = forcePeer;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
/* if peerMac is found, then it returns pointer to hddTdlsPeer_t
* otherwise, it returns NULL
*/
hddTdlsPeer_t *wlan_hdd_tdls_find_peer(hdd_adapter_t *pAdapter,
const u8 *mac,
tANI_BOOLEAN mutexLock)
{
u8 key;
struct list_head *pos;
struct list_head *head;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return NULL;
if ( mutexLock )
{
mutex_lock(&pHddCtx->tdls_lock);
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
key = wlan_hdd_tdls_hash_key(mac);
head = &pHddTdlsCtx->peer_list[key];
list_for_each(pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (!memcmp(mac, curr_peer->peerMac, 6)) {
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"findTdlsPeer: found staId %d", curr_peer->staId);
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
if ( mutexLock )
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
return NULL;
}
hddTdlsPeer_t *wlan_hdd_tdls_find_all_peer(hdd_context_t *pHddCtx,
const u8 *mac)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer= NULL;
VOS_STATUS status = 0;
mutex_lock(&pHddCtx->tdls_lock);
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer)
{
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
int wlan_hdd_tdls_reset_peer(hdd_adapter_t *pAdapter, const u8 *mac)
{
hdd_context_t *pHddCtx;
hddTdlsPeer_t *curr_peer;
int status = 0;
pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
if (0 != (wlan_hdd_validate_context(pHddCtx))) {
status = -EINVAL;
goto ret_status;
}
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = wlan_hdd_tdls_get_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
status = -EINVAL;
goto rel_lock;
}
/*
* Reset preferred offchannel and opclass for offchannel as
* per INI configuration only if peer is not forced one. For
* forced peer, offchannel and opclass is set in HAL API at the
* time of enabling TDLS for that specific peer and so do not overwrite
* those set by user space.
*/
if (FALSE == curr_peer->isForcedPeer) {
curr_peer->pref_off_chan_num = pHddCtx->cfg_ini->fTDLSPrefOffChanNum;
curr_peer->op_class_for_pref_off_chan =
wlan_hdd_find_opclass(WLAN_HDD_GET_HAL_CTX(pAdapter),
curr_peer->pref_off_chan_num,
pHddCtx->cfg_ini->fTDLSPrefOffChanBandwidth);
}
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED,
FALSE);
curr_peer->staId = 0;
rel_lock:
mutex_unlock(&pHddCtx->tdls_lock);
ret_status:
return status;
}
tANI_U16 wlan_hdd_tdlsConnectedPeers(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = NULL;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return 0;
}
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return 0;
EXIT();
return pHddCtx->connected_peer_count;
}
int wlan_hdd_tdls_get_all_peers(hdd_adapter_t *pAdapter, char *buf, int buflen)
{
int i;
int len, init_len;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
hdd_station_ctx_t *hdd_sta_ctx;
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return 0;
if ((WLAN_HDD_INFRA_STATION != pAdapter->device_mode)
&& (WLAN_HDD_P2P_CLIENT != pAdapter->device_mode)) {
len = scnprintf(buf, buflen, "\nNo TDLS support for this adapter");
return len;
}
hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
if (eConnectionState_Associated != hdd_sta_ctx->conn_info.connState) {
len = scnprintf(buf, buflen, "\nSTA is not associated\n");
return len;
}
init_len = buflen;
len = scnprintf(buf, buflen, "\n%-18s%-3s%-4s%-3s%-5s\n",
"MAC", "Id", "cap", "up", "RSSI");
buf += len;
buflen -= len;
/* 1234567890123456789012345678901234567 */
len = scnprintf(buf, buflen, "---------------------------------\n");
buf += len;
buflen -= len;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
mutex_unlock(&pHddCtx->tdls_lock);
len = scnprintf(buf, buflen, "TDLS not enabled\n");
return len;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer= list_entry (pos, hddTdlsPeer_t, node);
if (buflen < 32+1)
break;
len = scnprintf(buf, buflen,
MAC_ADDRESS_STR"%3d%4s%3s%5d\n",
MAC_ADDR_ARRAY(curr_peer->peerMac),
curr_peer->staId,
(curr_peer->tdls_support == eTDLS_CAP_SUPPORTED) ? "Y":"N",
TDLS_IS_CONNECTED(curr_peer) ? "Y":"N",
curr_peer->rssi);
buf += len;
buflen -= len;
}
}
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
return init_len-buflen;
}
void wlan_hdd_tdls_connection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx) || (NULL == pHddTdlsCtx))
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
mutex_lock(&pHddCtx->tdls_lock);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s, update %d discover %d", __func__,
pHddTdlsCtx->threshold_config.tx_period_t,
pHddTdlsCtx->threshold_config.discovery_period_t);
if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode) {
wlan_hdd_tdls_peer_reset_discovery_processed(pHddTdlsCtx);
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
}
mutex_unlock(&pHddCtx->tdls_lock);
}
void wlan_hdd_tdls_disconnection_callback(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"%s", __func__);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != wlan_hdd_validate_context(pHddCtx))
return;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx)
{
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
pHddTdlsCtx->discovery_sent_cnt = 0;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
wlan_hdd_tdls_monitor_timers_stop(pHddTdlsCtx);
wlan_hdd_tdls_free_list(pHddTdlsCtx, false);
pHddTdlsCtx->curr_candidate = NULL;
mutex_unlock(&pHddCtx->tdls_lock);
}
void wlan_hdd_tdls_mgmt_completion_callback(hdd_adapter_t *pAdapter, tANI_U32 statusCode)
{
pAdapter->mgmtTxCompletionStatus = statusCode;
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Mgmt TX Completion %d",
__func__, statusCode);
complete(&pAdapter->tdls_mgmt_comp);
}
void wlan_hdd_tdls_increment_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
mutex_lock(&pHddCtx->tdls_lock);
pHddCtx->connected_peer_count++;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: %d",
__func__, pHddCtx->connected_peer_count);
if (pHddCtx->cfg_ini->enable_dynamic_sta_chainmask &&
(HDD_ANTENNA_MODE_2X2 != pHddCtx->current_antenna_mode))
hdd_decide_dynamic_chain_mask(pHddCtx,
HDD_ANTENNA_MODE_2X2);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
void wlan_hdd_tdls_decrement_peer_count(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
mutex_lock(&pHddCtx->tdls_lock);
if (pHddCtx->connected_peer_count)
pHddCtx->connected_peer_count--;
wlan_hdd_tdls_check_power_save_prohibited(pAdapter);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: %d",
__func__, pHddCtx->connected_peer_count);
if (!pHddCtx->connected_peer_count &&
pHddCtx->cfg_ini->enable_dynamic_sta_chainmask)
hdd_decide_dynamic_chain_mask(pHddCtx,
HDD_ANTENNA_MODE_INVALID);
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
}
void wlan_hdd_tdls_check_bmps(hdd_adapter_t *pAdapter)
{
tdlsCtx_t *pHddTdlsCtx = NULL;
hdd_context_t *pHddCtx = NULL;
hddTdlsPeer_t *curr_peer;
VOS_STATUS status;
if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx) || (NULL == pHddTdlsCtx))
{
//getting over logged, so moving log-level to INFO.
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddCtx or pHddTdlsCtx points to NULL"));
return;
}
curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, TRUE);
if (NULL != curr_peer)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls in progress. Dont check for BMPS " MAC_ADDRESS_STR,
__func__, MAC_ADDR_ARRAY (curr_peer->peerMac));
return;
}
/*
* If Powersave Offload is enabled
* Fw will take care incase of concurrency
*/
if(!pHddCtx->cfg_ini->enablePowersaveOffload)
{
if ((TDLS_CTX_MAGIC != pHddCtx->tdls_scan_ctxt.magic) &&
(0 == pHddCtx->connected_peer_count) &&
(0 == pHddTdlsCtx->discovery_sent_cnt))
{
if (FALSE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: No TDLS peer connected/discovery sent. Enable BMPS",
__func__);
status = hdd_enable_bmps_imps(pHddCtx);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = false;
}
}
else
{
if (TRUE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS peer connected. Disable BMPS", __func__);
status = hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_INFRA_STATION);
if (status == VOS_STATUS_SUCCESS)
pHddTdlsCtx->is_tdls_disabled_bmps = true;
}
}
}
else
{
if ((TDLS_CTX_MAGIC != pHddCtx->tdls_scan_ctxt.magic) &&
(0 == pHddCtx->connected_peer_count) &&
(0 == pHddTdlsCtx->discovery_sent_cnt))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: No TDLS peer connected/discovery sent. Enable BMPS",
__func__);
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, FALSE);
sme_PsOffloadEnablePowerSave(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId);
}
else
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: TDLS peer connected. Disable BMPS", __func__);
sme_SetTdlsPowerSaveProhibited(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, TRUE);
sme_PsOffloadDisablePowerSave(WLAN_HDD_GET_HAL_CTX(pAdapter),
NULL, NULL,
pAdapter->sessionId);
}
}
return;
}
/* return pointer to hddTdlsPeer_t if TDLS is ongoing. Otherwise return NULL.
* mac - if NULL check for all the peer list, otherwise, skip this mac when skip_self is TRUE
* skip_self - if TRUE, skip this mac. otherwise, check all the peer list. if
mac is NULL, this argument is ignored, and check for all the peer list.
*/
static hddTdlsPeer_t *wlan_hdd_tdls_find_progress_peer(hdd_adapter_t *pAdapter,
const u8 *mac,
u8 skip_self)
{
int i;
struct list_head *head;
hddTdlsPeer_t *curr_peer;
struct list_head *pos;
tdlsCtx_t *pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);;
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return NULL;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (skip_self && mac && !memcmp(mac, curr_peer->peerMac, 6)) {
continue;
}
else
{
if (eTDLS_LINK_CONNECTING == curr_peer->link_status)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s:" MAC_ADDRESS_STR " eTDLS_LINK_CONNECTING",
__func__, MAC_ADDR_ARRAY(curr_peer->peerMac));
return curr_peer;
}
}
}
}
return NULL;
}
hddTdlsPeer_t *wlan_hdd_tdls_is_progress(hdd_context_t *pHddCtx,
const u8 *mac, u8 skip_self,
bool need_lock)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
hdd_adapter_t *pAdapter = NULL;
tdlsCtx_t *pHddTdlsCtx = NULL;
hddTdlsPeer_t *curr_peer= NULL;
VOS_STATUS status = 0;
if (need_lock)
mutex_lock(&pHddCtx->tdls_lock);
status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
while ( NULL != pAdapterNode && VOS_STATUS_SUCCESS == status )
{
pAdapter = pAdapterNode->pAdapter;
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL != pHddTdlsCtx)
{
curr_peer = wlan_hdd_tdls_find_progress_peer(pAdapter, mac, skip_self);
if (curr_peer)
{
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
return curr_peer;
}
}
status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
pAdapterNode = pNext;
}
if (need_lock)
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
void wlan_hdd_tdls_implicit_send_discovery_request(tdlsCtx_t * pHddTdlsCtx)
{
hdd_context_t *pHddCtx;
hddTdlsPeer_t *curr_peer;
hddTdlsPeer_t *temp_peer;
tSirMacAddr peer_mac;
ENTER();
if (NULL == pHddTdlsCtx)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("pHddTdlsCtx is NULL"));
return;
}
pHddCtx = WLAN_HDD_GET_CTX(pHddTdlsCtx->pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer = pHddTdlsCtx->curr_candidate;
if (NULL == curr_peer)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("pHddCtx is not valid"));
goto done;
}
vos_mem_copy(&peer_mac, curr_peer->peerMac, sizeof(peer_mac));
mutex_unlock(&pHddCtx->tdls_lock);
/*
* If Powersave Offload is enabled
* Fw will take care incase of concurrency
*/
if(!pHddCtx->cfg_ini->enablePowersaveOffload)
{
if (TRUE == sme_IsPmcBmps(WLAN_HDD_GET_HAL_CTX(pHddTdlsCtx->pAdapter)))
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Disable BMPS", __func__);
hdd_disable_bmps_imps(pHddCtx, WLAN_HDD_INFRA_STATION);
}
}
temp_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, TRUE);
if (NULL != temp_peer)
{
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO, "%s: " MAC_ADDRESS_STR " ongoing. pre_setup ignored",
__func__, MAC_ADDR_ARRAY(temp_peer->peerMac));
goto done;
}
if (eTDLS_CAP_UNKNOWN != curr_peer->tdls_support)
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERING,
eTDLS_LINK_SUCCESS,
TRUE);
mutex_lock(&pHddCtx->tdls_lock);
/* Ignore discovery attempt if External Control is enabled, that
* is, peer is forced. In that case, continue discovery attempt
* regardless attempt count
*/
if (FALSE == curr_peer->isForcedPeer)
{
if ((curr_peer->discovery_cycles_retry_cnt >=
MAX_TDLS_DISCOVERY_CYCLE_RETRIES) ||
((curr_peer->discovery_cycles_retry_cnt != 0) &&
(curr_peer->discovery_cycles_retry_cnt <
MAX_TDLS_DISCOVERY_CYCLE_RETRIES) &&
(adf_os_ticks_to_msecs(HDD_BW_GET_DIFF(jiffies,
curr_peer->last_discovery_req_cycle_ts)) <=
MIN_TDLS_DISCOVERY_CYCLE_RETRY_TIME))) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
"%s: discovery attempt (%d), discovery cycle retry count (%d) for peer "
MAC_ADDRESS_STR ", ignore discovery trigger from fw",
__func__, curr_peer->discovery_attempt,
curr_peer->discovery_cycles_retry_cnt,
MAC_ADDR_ARRAY(curr_peer->peerMac));
curr_peer->tdls_support = eTDLS_CAP_NOT_SUPPORTED;
/* Since TDLS discovery attempt reached the
* maximum threshold, so we remove the peer
* from the FW connection table.
*/
if (0 != wlan_hdd_tdls_update_peer_mac(pHddTdlsCtx->pAdapter,
curr_peer->peerMac, eSME_TDLS_PEER_REMOVE_MAC_ADDR))
hddLog(LOGE, FL("TDLS Peer mac update Failed "
MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(curr_peer->peerMac));
goto done;
}
}
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_DISCOVERING,
eTDLS_LINK_SUCCESS,
TRUE);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: Implicit TDLS, Send Discovery request event", __func__);
cfg80211_tdls_oper_request(pHddTdlsCtx->pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_DISCOVERY_REQ,
FALSE,
GFP_KERNEL);
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx->discovery_sent_cnt++;
curr_peer->discovery_attempt++;
wlan_hdd_tdls_check_power_save_prohibited(pHddTdlsCtx->pAdapter);
mutex_unlock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_check_bmps(pHddTdlsCtx->pAdapter);
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: discovery count %u timeout %u msec",
__func__, pHddTdlsCtx->discovery_sent_cnt,
pHddTdlsCtx->threshold_config.tx_period_t - TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
mutex_lock(&pHddCtx->tdls_lock);
wlan_hdd_tdls_timer_restart(pHddTdlsCtx->pAdapter,
&pHddTdlsCtx->peerDiscoveryTimeoutTimer,
pHddTdlsCtx->threshold_config.tx_period_t - TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
done:
pHddTdlsCtx->curr_candidate = NULL;
pHddTdlsCtx->magic = 0;
mutex_unlock(&pHddCtx->tdls_lock);
EXIT();
return;
}
int wlan_hdd_tdls_copy_scan_context(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
tdls_scan_context_t *scan_ctx;
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return -1;
scan_ctx = &pHddCtx->tdls_scan_ctxt;
scan_ctx->wiphy = wiphy;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
scan_ctx->dev = dev;
#endif
scan_ctx->scan_request = request;
EXIT();
return 0;
}
static void wlan_hdd_tdls_scan_init_work(hdd_context_t *pHddCtx,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
struct net_device *dev,
#endif
struct cfg80211_scan_request *request,
unsigned long delay)
{
if (TDLS_CTX_MAGIC != pHddCtx->tdls_scan_ctxt.magic)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
wlan_hdd_tdls_copy_scan_context(pHddCtx, wiphy, dev, request);
#else
wlan_hdd_tdls_copy_scan_context(pHddCtx, wiphy, request);
#endif
pHddCtx->tdls_scan_ctxt.attempt = 0;
pHddCtx->tdls_scan_ctxt.magic = TDLS_CTX_MAGIC;
}
schedule_delayed_work(&pHddCtx->tdls_scan_ctxt.tdls_scan_work, delay);
}
/* return negative = caller should stop and return error code immediately
return 0 = caller should stop and return success immediately
return 1 = caller can continue to scan
*/
int wlan_hdd_tdls_scan_callback (hdd_adapter_t *pAdapter,
struct wiphy *wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
struct net_device *dev,
#endif
struct cfg80211_scan_request *request)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
u16 connectedTdlsPeers;
hddTdlsPeer_t *curr_peer;
unsigned long delay;
int ret;
ENTER();
ret = wlan_hdd_validate_context(pHddCtx);
if (ret)
return ret;
/* if tdls is not enabled, then continue scan */
if (eTDLS_SUPPORT_NOT_ENABLED == pHddCtx->tdls_mode)
return 1;
curr_peer = wlan_hdd_tdls_is_progress(pHddCtx, NULL, 0, TRUE);
if (NULL != curr_peer)
{
if (pHddCtx->tdls_scan_ctxt.reject++ >= TDLS_MAX_SCAN_REJECT)
{
pHddCtx->tdls_scan_ctxt.reject = 0;
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: " MAC_ADDRESS_STR ". scan rejected %d. force it to idle",
__func__, MAC_ADDR_ARRAY (curr_peer->peerMac), pHddCtx->tdls_scan_ctxt.reject);
wlan_hdd_tdls_set_peer_link_status (curr_peer,
eTDLS_LINK_IDLE,
eTDLS_LINK_UNSPECIFIED,
TRUE);
return 1;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN,
"%s: tdls in progress. scan rejected %d",
__func__, pHddCtx->tdls_scan_ctxt.reject);
return -EBUSY;
}
/* tdls teardown is ongoing */
if (eTDLS_SUPPORT_DISABLED == pHddCtx->tdls_mode)
{
connectedTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (connectedTdlsPeers && (pHddCtx->tdls_scan_ctxt.attempt < TDLS_MAX_SCAN_SCHEDULE))
{
delay = (unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION*connectedTdlsPeers);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls disabled, but still connected_peers %d attempt %d. schedule scan %lu msec",
__func__, connectedTdlsPeers, pHddCtx->tdls_scan_ctxt.attempt, delay);
wlan_hdd_tdls_scan_init_work (pHddCtx, wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
dev,
#endif
request,
msecs_to_jiffies(delay));
/* scan should not continue */
return 0;
}
/* no connected peer or max retry reached, scan continue */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls disabled. connected_peers %d attempt %d. scan allowed",
__func__, connectedTdlsPeers, pHddCtx->tdls_scan_ctxt.attempt);
return 1;
}
/* while tdls is up, first time scan */
else if (eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)
{
/* Disable implicit trigger logic & tdls operation */
wlan_hdd_tdls_set_mode(pHddCtx, eTDLS_SUPPORT_DISABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_SCAN);
/* indicate the teardown all connected to peer */
connectedTdlsPeers = wlan_hdd_tdlsConnectedPeers(pAdapter);
if (connectedTdlsPeers)
{
tANI_U8 staIdx;
tANI_U8 num = 0;
tANI_U8 i;
tANI_BOOLEAN allPeersBufStas = 1;
hddTdlsPeer_t *curr_peer;
hddTdlsPeer_t *connectedPeerList[HDD_MAX_NUM_TDLS_STA];
/* If TDLSScan is enabled then allow scan and maintain tdls link
* regardless if peer is buffer sta capable or not and if device
* is sleep sta capable or not. If peer is not buffer sta capable,
* then Tx would stop when device initiates scan and there will be
* loss of Rx packets since peer would not know when device moves
* away from the tdls channel.
*/
if (1 == pHddCtx->cfg_ini->enable_tdls_scan) {
hddLog(LOG1,
FL("TDLSScan enabled, keep tdls link and allow scan, connectedTdlsPeers: %d"),
connectedTdlsPeers);
return 1;
}
for (staIdx = 0; staIdx < pHddCtx->max_num_tdls_sta; staIdx++)
{
if (pHddCtx->tdlsConnInfo[staIdx].staId)
{
curr_peer = wlan_hdd_tdls_find_all_peer(pHddCtx,
pHddCtx->tdlsConnInfo[staIdx].peerMac.bytes);
if (curr_peer)
{
connectedPeerList[num++] = curr_peer;
if (!(curr_peer->isBufSta))
allPeersBufStas = 0;
}
}
}
if ((TDLS_MAX_CONNECTED_PEERS_TO_ALLOW_SCAN ==
connectedTdlsPeers) &&
(pHddCtx->cfg_ini->fEnableTDLSSleepSta) &&
(allPeersBufStas))
{
/* All connected peers bufStas and we can be sleepSta
* so allow scan
*/
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: All peers (num %d) bufSTAs, we can be sleep sta, so allow scan, tdls mode changed to %d",
__func__, connectedTdlsPeers, pHddCtx->tdls_mode);
return 1;
}
else
{
for (i = 0; i < num; i++)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: indicate TDLS teadown (staId %d)",
__func__, connectedPeerList[i]->staId);
#ifdef CONFIG_TDLS_IMPLICIT
wlan_hdd_tdls_indicate_teardown(
connectedPeerList[i]->pHddTdlsCtx->pAdapter,
connectedPeerList[i],
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(eTDLS_TEARDOWN_SCAN,
connectedPeerList[i]->peerMac);
#endif
}
}
/* schedule scan */
delay = (unsigned long)(TDLS_DELAY_SCAN_PER_CONNECTION*connectedTdlsPeers);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls enabled (mode %d), connected_peers %d. schedule scan %lu msec",
__func__, pHddCtx->tdls_mode, wlan_hdd_tdlsConnectedPeers(pAdapter),
delay);
wlan_hdd_tdls_scan_init_work (pHddCtx, wiphy,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)) && !defined(WITH_BACKPORTS)
dev,
#endif
request,
msecs_to_jiffies(delay));
/* scan should not continue */
return 0;
}
/* no connected peer, scan continue */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: tdls_mode %d, and no tdls connection. scan allowed",
__func__, pHddCtx->tdls_mode);
}
EXIT();
return 1;
}
void wlan_hdd_tdls_scan_done_callback(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
ENTER();
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return;
/* free allocated memory at scan time */
wlan_hdd_tdls_free_scan_request (&pHddCtx->tdls_scan_ctxt);
/* if tdls was enabled before scan, re-enable tdls mode */
if(eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode_last ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode_last ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode_last)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
("%s: revert tdls mode %d"), __func__, pHddCtx->tdls_mode_last);
wlan_hdd_tdls_set_mode(pHddCtx, pHddCtx->tdls_mode_last, FALSE,
HDD_SET_TDLS_MODE_SOURCE_SCAN);
}
wlan_hdd_tdls_check_bmps(pAdapter);
EXIT();
}
void wlan_hdd_tdls_timer_restart(hdd_adapter_t *pAdapter,
vos_timer_t *timer,
v_U32_t expirationTime)
{
hdd_station_ctx_t *pHddStaCtx;
if (NULL == pAdapter || WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("invalid pAdapter: %pK"), pAdapter);
return;
}
pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
/* Check whether driver load unload is in progress */
if (vos_is_load_unload_in_progress(VOS_MODULE_ID_VOSS, NULL)) {
hddLog(LOGE, FL("Driver load/unload is in progress."));
return;
}
if (hdd_connIsConnected(pHddStaCtx)) {
vos_timer_stop(timer);
vos_timer_start(timer, expirationTime);
}
}
void wlan_hdd_tdls_indicate_teardown(hdd_adapter_t *pAdapter,
hddTdlsPeer_t *curr_peer,
tANI_U16 reason)
{
if ((NULL == pAdapter || WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic) ||
(NULL == curr_peer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("parameters passed are invalid"));
return;
}
if (eTDLS_LINK_CONNECTED != curr_peer->link_status)
return;
wlan_hdd_tdls_set_peer_link_status(curr_peer,
eTDLS_LINK_TEARING,
eTDLS_LINK_UNSPECIFIED,
TRUE);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Indicating NL80211_TDLS_TEARDOWN to supplicant, reason %d"),
reason);
cfg80211_tdls_oper_request(pAdapter->dev,
curr_peer->peerMac,
NL80211_TDLS_TEARDOWN,
reason,
GFP_KERNEL);
}
/*EXT TDLS*/
int wlan_hdd_set_callback(hddTdlsPeer_t *curr_peer,
cfg80211_exttdls_callback callback)
{
hdd_context_t *pHddCtx;
hdd_adapter_t *pAdapter;
if (!curr_peer) return -1;
pAdapter = curr_peer->pHddTdlsCtx->pAdapter;
pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if ((NULL == pHddCtx)) return -1;
mutex_lock(&pHddCtx->tdls_lock);
curr_peer->state_change_notification = callback;
mutex_unlock(&pHddCtx->tdls_lock);
return 0;
}
void wlan_hdd_tdls_get_wifi_hal_state(hddTdlsPeer_t *curr_peer,
tANI_U32 *state,
tANI_S32 *reason)
{
hdd_context_t *hddctx;
hdd_adapter_t *adapter;
if (!curr_peer) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
return;
}
adapter = curr_peer->pHddTdlsCtx->pAdapter;
hddctx = WLAN_HDD_GET_CTX(adapter);
if (0 != (wlan_hdd_validate_context(hddctx)))
return;
*reason = curr_peer->reason;
switch(curr_peer->link_status)
{
case eTDLS_LINK_IDLE:
case eTDLS_LINK_DISCOVERED:
*state = QCA_WIFI_HAL_TDLS_ENABLED;
break;
case eTDLS_LINK_DISCOVERING:
case eTDLS_LINK_CONNECTING:
*state = QCA_WIFI_HAL_TDLS_ENABLED;
break;
case eTDLS_LINK_CONNECTED:
if ((hddctx->cfg_ini->fEnableTDLSOffChannel) &&
(hddctx->tdls_fw_off_chan_mode == ENABLE_CHANSWITCH))
*state = QCA_WIFI_HAL_TDLS_ESTABLISHED_OFF_CHANNEL;
else
*state = QCA_WIFI_HAL_TDLS_ESTABLISHED;
break;
case eTDLS_LINK_TEARING:
*state = QCA_WIFI_HAL_TDLS_DROPPED;
break;
}
}
int wlan_hdd_tdls_get_status(hdd_adapter_t *pAdapter,
const tANI_U8* mac,
uint32_t *opclass,
uint32_t *channel,
tANI_U32 *state,
tANI_S32 *reason)
{
hddTdlsPeer_t *curr_peer;
hdd_context_t *hddctx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(hddctx)))
return -EINVAL;
mutex_lock(&hddctx->tdls_lock);
curr_peer = wlan_hdd_tdls_find_peer(pAdapter, mac, FALSE);
if (curr_peer == NULL) {
mutex_unlock(&hddctx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is NULL"));
*state = QCA_WIFI_HAL_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
return -EINVAL;
}
if (hddctx->cfg_ini->fTDLSExternalControl &&
(FALSE == curr_peer->isForcedPeer)) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("curr_peer is not Forced"));
*state = QCA_WIFI_HAL_TDLS_DISABLED;
*reason = eTDLS_LINK_UNSPECIFIED;
} else {
wlan_hdd_tdls_determine_channel_opclass(hddctx, pAdapter, curr_peer,
channel, opclass);
wlan_hdd_tdls_get_wifi_hal_state(curr_peer, state, reason);
}
mutex_unlock(&hddctx->tdls_lock);
return 0;
}
static hddTdlsPeer_t *
wlan_hdd_tdls_find_first_connected_peer(hdd_adapter_t *pAdapter)
{
int i;
struct list_head *head;
struct list_head *pos;
hddTdlsPeer_t *curr_peer = NULL;
tdlsCtx_t *pHddTdlsCtx;
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
if (0 != (wlan_hdd_validate_context(pHddCtx)))
return NULL;
mutex_lock(&pHddCtx->tdls_lock);
pHddTdlsCtx = WLAN_HDD_GET_TDLS_CTX_PTR(pAdapter);
if (NULL == pHddTdlsCtx) {
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
for (i = 0; i < TDLS_PEER_LIST_SIZE; i++) {
head = &pHddTdlsCtx->peer_list[i];
list_for_each(pos, head) {
curr_peer = list_entry (pos, hddTdlsPeer_t, node);
if (curr_peer && (curr_peer->link_status == eTDLS_LINK_CONNECTED)) {
mutex_unlock(&pHddCtx->tdls_lock);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL(MAC_ADDRESS_STR "eTDLS_LINK_CONNECTED"),
MAC_ADDR_ARRAY(curr_peer->peerMac));
return curr_peer;
}
}
}
mutex_unlock(&pHddCtx->tdls_lock);
return NULL;
}
int hdd_set_tdls_offchannel(hdd_context_t *pHddCtx, int offchannel)
{
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)) {
if (offchannel < CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM_MIN ||
offchannel > CFG_TDLS_PREFERRED_OFF_CHANNEL_NUM_MAX) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid tdls off channel %u"),
offchannel);
return -EINVAL;
}
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Either TDLS or TDLS Off-channel is not enabled"));
return -ENOTSUPP;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("change tdls off channel from %d to %d"),
pHddCtx->tdls_off_channel, offchannel);
pHddCtx->tdls_off_channel = offchannel;
return 0;
}
int hdd_set_tdls_secoffchanneloffset(hdd_context_t *pHddCtx, int offchanoffset)
{
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)) {
pHddCtx->tdls_channel_offset = 0;
switch (offchanoffset) {
case TDLS_SEC_OFFCHAN_OFFSET_0:
pHddCtx->tdls_channel_offset = (1 << BW_20_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_40PLUS:
case TDLS_SEC_OFFCHAN_OFFSET_40MINUS:
pHddCtx->tdls_channel_offset = (1 << BW_40_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_80:
pHddCtx->tdls_channel_offset = (1 << BW_80_OFFSET_BIT);
break;
case TDLS_SEC_OFFCHAN_OFFSET_160:
pHddCtx->tdls_channel_offset = (1 << BW_160_OFFSET_BIT);
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid tdls secondary off channel offset %d"),
offchanoffset);
return -EINVAL;
}/* end switch */
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Either TDLS or TDLS Off-channel is not enabled"));
return -ENOTSUPP;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("change tdls secondary off channel offset to 0x%x"),
pHddCtx->tdls_channel_offset);
return 0;
}
int hdd_set_tdls_offchannelmode(hdd_adapter_t *pAdapter, int offchanmode)
{
hddTdlsPeer_t *connPeer = NULL;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
tSmeTdlsChanSwitchParams chanSwitchParams;
eHalStatus status = eHAL_STATUS_FAILURE;
if (offchanmode < ENABLE_CHANSWITCH || offchanmode > DISABLE_CHANSWITCH) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Invalid tdls off channel mode %d"),
offchanmode);
return -EINVAL;
}
if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("tdls off channel mode req in not associated state %d"),
offchanmode);
return -EPERM;
}
if ((TRUE == pHddCtx->cfg_ini->fEnableTDLSOffChannel) &&
(eTDLS_SUPPORT_ENABLED == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXTERNAL_CONTROL == pHddCtx->tdls_mode ||
eTDLS_SUPPORT_EXPLICIT_TRIGGER_ONLY == pHddCtx->tdls_mode)) {
connPeer = wlan_hdd_tdls_find_first_connected_peer(pAdapter);
if (NULL == connPeer) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
FL("No TDLS Connected Peer"));
return -EPERM;
}
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
FL("TDLS Connection not supported"));
return -ENOTSUPP;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("TDLS Channel Switch in swmode=%d tdls_off_channel %d offchanoffset %d"),
offchanmode, pHddCtx->tdls_off_channel,
pHddCtx->tdls_channel_offset);
switch (offchanmode) {
case ENABLE_CHANSWITCH:
if (pHddCtx->tdls_off_channel && pHddCtx->tdls_channel_offset) {
chanSwitchParams.tdls_off_channel = pHddCtx->tdls_off_channel;
chanSwitchParams.tdls_off_ch_bw_offset =
pHddCtx->tdls_channel_offset;
chanSwitchParams.opclass =
wlan_hdd_find_opclass(WLAN_HDD_GET_HAL_CTX(pAdapter),
chanSwitchParams.tdls_off_channel,
chanSwitchParams.tdls_off_ch_bw_offset);
} else {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("TDLS off-channel parameters are not set yet!!!"));
return -EINVAL;
}
break;
case DISABLE_CHANSWITCH:
chanSwitchParams.tdls_off_channel = 0;
chanSwitchParams.tdls_off_ch_bw_offset = 0;
chanSwitchParams.opclass = 0;
break;
default:
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Incorrect Parameters mode: %d tdls_off_channel: %d offchanoffset: %d"),
offchanmode, pHddCtx->tdls_off_channel,
pHddCtx->tdls_channel_offset);
return -EINVAL;
}/* end switch */
chanSwitchParams.vdev_id = pAdapter->sessionId;
chanSwitchParams.tdls_off_ch_mode = offchanmode;
chanSwitchParams.is_responder = connPeer->is_responder;
vos_mem_copy(&chanSwitchParams.peer_mac_addr,
&connPeer->peerMac, sizeof(tSirMacAddr));
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
FL("Peer " MAC_ADDRESS_STR " vdevId: %d, off channel: %d, offset: %d, mode: %d, is_responder: %d"),
MAC_ADDR_ARRAY(chanSwitchParams.peer_mac_addr),
chanSwitchParams.vdev_id,
chanSwitchParams.tdls_off_channel,
chanSwitchParams.tdls_off_ch_bw_offset,
chanSwitchParams.tdls_off_ch_mode,
chanSwitchParams.is_responder);
status = sme_SendTdlsChanSwitchReq(WLAN_HDD_GET_HAL_CTX(pAdapter),
&chanSwitchParams);
if (status != eHAL_STATUS_SUCCESS) {
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
FL("Failed to send channel switch request to sme"));
return -EINVAL;
}
pHddCtx->tdls_fw_off_chan_mode = offchanmode;
if (ENABLE_CHANSWITCH == offchanmode) {
connPeer->pref_off_chan_num = chanSwitchParams.tdls_off_channel;
connPeer->op_class_for_pref_off_chan = chanSwitchParams.opclass;
}
return 0;
}
/**
* wlan_hdd_tdls_teardown_links() - teardown tdls links
* @hddCtx : pointer to hdd context
*
* Return: 0 if success else non zero
*/
static int wlan_hdd_tdls_teardown_links(hdd_context_t *hddctx, uint32_t mode)
{
uint16_t connected_tdls_peers = 0;
uint8_t staidx;
int ret = 0;
hddTdlsPeer_t *curr_peer = NULL;
hdd_adapter_t *adapter = NULL;
if (eTDLS_SUPPORT_NOT_ENABLED == hddctx->tdls_mode) {
hddLog(LOG1, FL("TDLS mode is disabled OR not enabled in FW"));
return 0;
}
adapter = hdd_get_adapter(hddctx, WLAN_HDD_INFRA_STATION);
if (adapter == NULL) {
hddLog(LOGE, FL("Station Adapter Not Found"));
return 0;
}
connected_tdls_peers = wlan_hdd_tdlsConnectedPeers(adapter);
if (!connected_tdls_peers)
return 0;
for (staidx = 0; staidx < hddctx->max_num_tdls_sta;
staidx++) {
if (!hddctx->tdlsConnInfo[staidx].staId)
continue;
curr_peer = wlan_hdd_tdls_find_all_peer(hddctx,
hddctx->tdlsConnInfo[staidx].peerMac.bytes);
if (!curr_peer)
continue;
/* Check, is connected peer supports more than one stream */
if (curr_peer->spatial_streams == TDLS_NSS_1x1_MODE)
continue;
hddLog(LOG1, FL("indicate TDLS teardown (staId %d)"),
curr_peer->staId);
wlan_hdd_tdls_indicate_teardown(
curr_peer->pHddTdlsCtx->pAdapter,
curr_peer,
eSIR_MAC_TDLS_TEARDOWN_UNSPEC_REASON);
hdd_send_wlan_tdls_teardown_event(
eTDLS_TEARDOWN_ANTENNA_SWITCH,
curr_peer->peerMac);
mutex_lock(&hddctx->tdls_lock);
hddctx->tdls_teardown_peers_cnt++;
mutex_unlock(&hddctx->tdls_lock);
}
mutex_lock(&hddctx->tdls_lock);
if (hddctx->tdls_teardown_peers_cnt >= 1) {
hddctx->tdls_nss_switch_in_progress = true;
hddLog(LOG1, FL("TDLS peers to be torn down = %d"),
hddctx->tdls_teardown_peers_cnt);
/* Antenna switch 2x2 to 1x1 */
if (mode == HDD_ANTENNA_MODE_1X1) {
hddctx->tdls_nss_transition_mode =
TDLS_NSS_TRANSITION_2x2_to_1x1;
ret = -EAGAIN;
} else {
/* Antenna switch 1x1 to 2x2 */
hddctx->tdls_nss_transition_mode =
TDLS_NSS_TRANSITION_1x1_to_2x2;
ret = 0;
}
hddLog(LOG1,
FL("TDLS teardown for antenna switch operation starts"));
}
mutex_unlock(&hddctx->tdls_lock);
return ret;
}
/**
* wlan_hdd_tdls_antenna_switch() - Dynamic TDLS antenna switch 1x1 <-> 2x2
* antenna mode in standalone station
* @hdd_ctx: Pointer to hdd contex
* @adapter: Pointer to hdd adapter
*
* Return: 0 if success else non zero
*/
int wlan_hdd_tdls_antenna_switch(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter, uint32_t mode)
{
hdd_station_ctx_t *sta_ctx = &adapter->sessionCtx.station;
uint8_t tdls_peer_cnt;
uint32_t vdev_nss;
hdd_config_t *cfg_ini = hdd_ctx->cfg_ini;
/* Check whether TDLS antenna switch is in progress */
if (hdd_ctx->tdls_nss_switch_in_progress) {
if (hdd_ctx->tdls_nss_teardown_complete == false) {
hddLog(LOGE, FL("TDLS nss switch is in progress"));
return -EAGAIN;
} else {
goto tdls_ant_sw_done;
}
}
/* Check whether TDLS is connected or not */
mutex_lock(&hdd_ctx->tdls_lock);
tdls_peer_cnt = hdd_ctx->connected_peer_count;
mutex_unlock(&hdd_ctx->tdls_lock);
if (tdls_peer_cnt <= 0) {
hddLog(LOG1, FL("No TDLS connection established"));
goto tdls_ant_sw_done;
}
/* Check the supported nss for TDLS */
if (IS_5G_CH(sta_ctx->conn_info.operationChannel))
vdev_nss = CFG_TDLS_NSS(cfg_ini->vdev_type_nss_5g);
else
vdev_nss = CFG_TDLS_NSS(cfg_ini->vdev_type_nss_2g);
if (vdev_nss == HDD_ANTENNA_MODE_1X1) {
hddLog(LOG1,
FL("Supported NSS is 1X1, no need to teardown TDLS links"));
goto tdls_ant_sw_done;
}
/* teardown all the tdls connections */
return wlan_hdd_tdls_teardown_links(hdd_ctx, mode);
tdls_ant_sw_done:
return 0;
}
/**
* hdd_set_tdls_scan_type - set scan during active tdls session
* @hdd_ctx: ptr to hdd context.
* @val: scan type value: 0 or 1.
*
* Set scan type during tdls session. If set to 1, that means driver
* shall maintain tdls link and allow scan regardless if tdls peer is
* buffer sta capable or not and/or if device is sleep sta capable or
* not. If tdls peer is not buffer sta capable then during scan there
* will be loss of Rx packets and Tx would stop when device moves away
* from tdls channel. If set to 0, then driver shall teardown tdls link
* before initiating scan if peer is not buffer sta capable and device
* is not sleep sta capable. By default, scan type is set to 0.
*
* Return: success (0) or failure (errno value)
*/
int hdd_set_tdls_scan_type(hdd_context_t *hdd_ctx, int val)
{
if ((val != 0) && (val != 1)) {
hddLog(LOGE, FL("Incorrect value of tdls scan type: %d"),
val);
return -EINVAL;
} else {
hdd_ctx->cfg_ini->enable_tdls_scan = val;
return 0;
}
}
/**
* wlan_hdd_change_tdls_mode - Change TDLS mode
* @data: void pointer
*
* Return: None
*/
void wlan_hdd_change_tdls_mode(void *data)
{
hdd_context_t *hdd_ctx = (hdd_context_t *)data;
wlan_hdd_tdls_set_mode(hdd_ctx, eTDLS_SUPPORT_ENABLED, FALSE,
HDD_SET_TDLS_MODE_SOURCE_OFFCHANNEL);
}
/**
* wlan_hdd_start_stop_tdls_source_timer - Restart TDLS source timer
* @pHddCtx: ptr to hdd context.
* @tdls_mode: value for TDLS support mode.
*
* This timer is the handler for avoiding overlapping for P2P listen
* and TDLS operation. This handler will start the source timer for
* the duration of tdls_enable_defer_time after P2P listen ends and
* before enabling TDLS again.
*
* Return: None
*/
void wlan_hdd_start_stop_tdls_source_timer(hdd_context_t *pHddCtx,
eTDLSSupportMode tdls_mode)
{
if (VOS_TIMER_STATE_RUNNING ==
vos_timer_getCurrentState(&pHddCtx->tdls_source_timer))
vos_timer_stop(&pHddCtx->tdls_source_timer);
if (tdls_mode == eTDLS_SUPPORT_DISABLED)
wlan_hdd_tdls_set_mode(pHddCtx, tdls_mode, FALSE,
HDD_SET_TDLS_MODE_SOURCE_OFFCHANNEL);
vos_timer_start(&pHddCtx->tdls_source_timer,
pHddCtx->cfg_ini->tdls_enable_defer_time);
return;
}